您的位置:首页 > 运维架构 > Linux

linux kernel时间管理

2014-07-24 16:15 218 查看

1. 内核中的时间节拍

内核时钟频率在arm中默认是100,即HZ=100,定义在arch/arm/include/asm/param.h,这个值是可以改变的,内核代码尽量使用HZ宏,不要直接使用100。提高频率虽然可以提高时钟精确度,响应速度,但也会因频繁中断导致系统负担增大。在时间转化的时候,系统会强制HZ能被1000整除。

jiffies是内核全局变量,系统启动是赋值为0,即每秒时钟中断100次,每个节拍间隔为0.1s,jiffies用来记录系统自启动以来的节拍总数。如果要记录自系统启动以后经过时秒数,可以这样转换jiffies/100。

2. jiffies

在linux/jiffies.h中定义

extern unsigned long volatile jiffies;//历史原因的jiffies变量,32bits

extern u64 jiffies_64;//为了防止溢出新定义的jiffies变量,64bits

在默认HZ=100的情况下,jiffies在497天后溢出,jiffes_64有生之年是看不到他溢出的。

在arch/arm/kernel/vmlinux.lds.S中有

jiffies = jiffies_64;

由于历史的原因保留jiffies这个变量名称,它是取64位的jiffies_64低32位,而在linux内核时间管理部分的代码使用整个64位,这样既可以保持兼容性,亦避免时间溢出。

获取jiffies使用如下函数u64 get_jiffies_64(void),这个函数实际上是return (u64)jiffies;因jiffies有volatile修饰,而jiffies_64没有,所以不要直接访问jiffies_64。

2.1 jiffies和时间的转换

这个转换尽量使用内核提供的接口,不要手动去转换,即使转换很简单。另外要注意在32位的jiffies乘时,可能会溢出。

jiffies到毫秒、微秒的互相转换:

extern unsigned int jiffies_to_msecs(const unsigned long j);

extern unsigned int jiffies_to_usecs(const unsigned long j);

extern unsigned long msecs_to_jiffies(const unsigned int m);

extern unsigned long usecs_to_jiffies(const unsigned int u);

jiffies到struct timespec、struct timeval互相转换:

<span style="font-size:12px;">struct timespec {
__kernel_time_t	tv_sec;			/* seconds */
long		tv_nsec;		/* nanoseconds */
};
struct timeval {
__kernel_time_t		tv_sec;		/* seconds */
__kernel_suseconds_t	tv_usec;	/* microseconds */
};</span>

extern unsigned long timespec_to_jiffies(const struct timespec *value);

extern void jiffies_to_timespec(const unsigned long jiffies, struct timespec *value);

extern unsigned long timeval_to_jiffies(const struct timeval *value);

extern void jiffies_to_timeval(const unsigned long jiffies, struct timeval *value);

获取本地时间:

extern void do_gettimeofday(struct timeval *tv);

extern void getnstimeofday(struct timespec *tv);

3. 内核定时器

底半部机制就是将工作推后执行,但是没有个量的概念,反正试不在当前执行就可以,而有时候我们需要将一些任务推后XX具体的时间后再执行,这个时候用内核定时器就是理想的解决方法,定时器处理函数在软中断执行。定时器和work_queue一样调用完后自动停止,除非再次激活触发。

相关文件:#include<linux/timer.h>

struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;//定时器列表入口
unsigned long expires;//超时时间(jiffies为单位)
struct tvec_base *base;//用户不使用

void (*function)(unsigned long);//超时后处理函数
unsigned long data;//传给超时处理函数的参数

int slack;

#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};


3.1 定义:

如:static struct timer_list mytimer;

3.2 初始化:

如:init_timer(&mytimer);

初始化了entry,base,slack

3.3 超时处理函数

如:void mytimer_handle(unsigned long arg);

这个arg就是timer_list.data的值

3.4 进一步初始化timer_list

到此为止还有超时时间expires和超时处理函数function还没有赋值。

如:

mytimer.function = mytimer_handle;

mytimer.expires = jiffies+HZ;

mytimer.data = (unsigned long)NULL;

也可以静态定义初始化一步搞定,替代上面4步:

#define DEFINE_TIMER(_name, _function, _expires, _data)		\
struct timer_list _name =				\
TIMER_INITIALIZER(_function, _expires, _data)


3.5 激活

定时器初始化后,激活了才能工作。

如:add_timer(&mytimer);

或者mod_timer(&mytimer, jiffies+HZ);//1s后超时处理,可在定时器超时处理函数中再次调用达到定量周期性处理

/**
* add_timer - start a timer
* @timer: the timer to be added
*
* The kernel will do a ->function(->data) callback from the
* timer interrupt at the ->expires point in the future. The
* current time is 'jiffies'.
*
* The timer's ->expires, ->function (and if the handler uses it, ->data)
* fields must be set prior calling this function.
*
* Timers with an ->expires field in the past will be executed in the next
* timer tick.
*/
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}

3.6 停止

定时器到期之前停止定时器,其实是否到期都可以调用,如果调用时未激活返回0,否则返回1。定时器到期后,自动会停止的。

在SMP上,可能定时器中断在其他定时器上运行了,所以删除定时器时要等待其他处理器的定时器处理函数退出后,因此del_timer_sync()函数可能会阻塞,因此在中断上下文不能用这个函数,del_timer可以在中断上下文使用。

int del_timer(struct timer_list *timer);

int del_timer_sync(struct timer_list *timer);

如:del_timer(&mytimer);

4. 内核延时

内核延时有中断底半部,推后一定时间工作的定时器,但是很多时候驱动需要一些硬件上的延时,而且这些延时都很短。

4.1 忙等待

如:下面的延时1s

unsigned long delay = jiffies + HZ;

//OTHER JOB

while(timer_before(jiffies, delay));

4.2 短延时

基本原理是基于忙等待,这个短延时,不是休眠,不像sleep长延时会有调度时间,独占当前的CPU后能立即得到执行,所以尽量不要延迟很长时间,特别是mdelay,。

void ndelay(unsigned long x);//纳秒延时

void udelay(unsigned long x);//微秒延时

void mdelay(unsigned long x);//毫秒延时

4.3 休眠延时(schedule_timeout)

extern signed long schedule_timeout(signed long timeout);//休眠延时

extern signed long schedule_timeout_interruptible(signed long timeout);//可被信号中断唤醒

extern signed long schedule_timeout_killable(signed long timeout);//可被SIGKILL中断唤醒

extern signed long schedule_timeout_uninterruptible(signed long timeout);//不可提前被唤醒

void msleep(unsigned int msecs)//通过schedule_timeout_uninterruptible()实现的,还有usleep,ssleep是微秒,秒的休眠

如果不要求时间延时很准确,可以使用定时器和休眠延时的方式,如果既要时间短又要时间精确可以使用忙等待或短延时。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: