当前位置:文档之家› linux定时器和Jiffies

linux定时器和Jiffies

1.linux HZLinux核心几个重要跟时间有关的名词或变数,以下将介绍HZ、tick与jiffies。

HZLinux核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来定义每一秒有几次timer interrupts。

举例来说,HZ为1000,代表每秒有1000次timer interrupts。

HZ可在编译核心时设定,如下所示(以核心版本adrian@adrian-desktop:~$ cd /usr/src/linuxadrian@adrian-desktop:/usr/src/linux$ make menuconfigProcessor type and features ---> Timer frequency (250 HZ) --->其中HZ可设定100、250、300或1000。

小实验观察/proc/interrupt的timer中断次数,并于一秒后再次观察其值。

理论上,两者应该相差250左右。

adrian@adrian-desktop:~$ cat /proc/interrupts | grep timer && sleep 1 && cat /proc/interrupts | grep timer0: 9309306 IO-APIC-edge timer0: 9309562 IO-APIC-edge timer上面四个栏位分别为中断号码、CPU中断次数、PIC与装置名称。

要检查系统上HZ的值是什么,就执行命令cat kernel/.config | grep '^CONFIG_HZ='2.TickTick是HZ的倒数,意即timer interrupt每发生一次中断的时间。

如HZ为250时,tick为4毫秒(millisecond)。

3.JiffiesJiffies为Linux核心变数(unsigned long),它被用来记录系统自开机以来,已经过了多少tick。

每发生一次timer interrupt,Jiffies变数会被加一。

值得注意的是,Jiffies于系统开机时,并非初始化成零,而是被设为-300*HZ (arch/i386/kernel/time.c),即代表系统于开机五分钟后,jiffies 便会溢位。

那溢位怎么办?事实上,Linux核心定义几个macro(timer_after、time_after_eq、time_before与time_before_eq),即便是溢位,也能借由这几个macro正确地取得jiffies的内容。

另外,80x86架构定义一个与jiffies相关的变数jiffies_64 ,此变数64位元,要等到此变数溢位可能要好几百万年。

因此要等到溢位这刻发生应该很难吧。

3.1 jiffies及其溢出全局变量jiffies取值为自操作系统启动以来的时钟滴答的数目,在头文件<linux/sched.h>中定义,数据类型为unsigned long volatile (32位无符号长整型)。

jiffies转换为秒可采用公式:(jiffies/HZ)计算,将秒转换为jiffies可采用公式:(seconds*HZ)计算。

当时钟中断发生时,jiffies 值就加1。

因此连续累加一年又四个多月后就会溢出(假定HZ=100,1个jiffies等于1/100秒,jiffies可记录的最大秒数为 (2^32 -1)/100=.95秒,约合497天或1.38年),即当取值到达最大值时继续加1,就变为了0。

3.4 Linux内核如何来防止jiffies溢出Linux内核中提供了以下四个宏,可有效解决由于jiffies溢出而造成程序逻辑出错的情况。

下面是从Linux Kernel/** These inlines deal with timer wrapping correctly. You are* strongly encouraged to use them* 1. Because people otherwise forget* 2. Because if the timer wrap changes in future you won't have to * alter your driver code.** time_after(a,b) returns true if the time a is after time b.** Do this with "<0" and ">=0" to only test the sign of the result. A * good compiler would generate better code (and a really good compiler * wouldn't care). Gcc is currently neither.*/#define time_after(a,b) \(typecheck(unsigned long, a) && \typecheck(unsigned long, b) && \((long)(b) - (long)(a) < 0))#define time_before(a,b) time_after(b,a)#define time_after_eq(a,b) \(typecheck(unsigned long, a) && \typecheck(unsigned long, b) && \((long)(a) - (long)(b) >= 0))#define time_before_eq(a,b) time_after_eq(b,a)在宏time_after中,首先确保两个输入参数a和b的数据类型为unsigned long,然后才执行实际的比较。

8. 结论系统中采用jiffies来计算时间,但由于jiffies溢出可能造成时间比较的错误,因而强烈建议在编码中使用 time_after等宏来比较时间先后关系,这些宏可以放心使用。

内核时钟:内核使用硬件提供的不同时钟来提供依赖于时间的服务,如busy-waiting(浪费CPU周期)和sleep-waiting(放弃CPU)5.HZ and Jiffiesjiffies记录了系统启动后的滴答数,常用的函数:time_before()、time_after()、time_after_eq()、time_before_eq()。

因为jiffies随时钟滴答变化,不能用编译器优化它,应取volatile值。

32位jiffies变量会在50天后溢出,太小,因此内核提供变量jiffies_64来hold 64位jiffies。

该64位的低32位即为jiffies,在32位机上需要两天指令来赋值64位数据,不是原子的,因此内核提供函数get_jiffies_64()。

6.Long Delaysbusy-wait:timebefore(),使CPU忙等待;sleep-wait:shedule_timeout(截至时间);无论在内核空间还是用户空间,都没有比HZ 更精确的控制了,因为时间片都是根据滴答更新的,而且即使定义了您的进程在超过指定时间后运行,调度器也可能根据优先级选择其他进程执行。

sleep-wait():wait_event_timeout()用于在满足某个条件或超时后重新执行,msleep()睡眠指定的ms后重新进入就绪队列,这些长延迟仅适用于进程上下文,在中断上下文中不能睡眠也不能长时间busy-waiting。

内核提供了timer API来在一定时间后执行某个函数:#include <linux/timer.h>struct timer_list my_timer;init_timer(&my_timer); /* Also see setup_timer() */my_timer.expire = jiffies + n*HZ; /* n is the timeout in number of seconds */my_timer.function = timer_func; /* Function to executeafter n seconds */my_timer.data = func_parameter; /* Parameter to be passed to timer_func */add_timer(&my_timer); /*Start the timer*/如果您想周期性执行上述代码,那么把它们加入timer_func()函数。

您使用mod_timer()来改变my_timer的超时值,del_timer()来删掉my_timer,用timer_pending()查看是否my_timer处于挂起状态。

用户空间函数clock_settime()和clock_gettime()用于获取内核时钟服务。

用户应用程序使用setitimer()和getitimer()来控制alarm信号的传递当指定超时发生后。

8.Real Time ClockRTC时钟track绝对时间。

RTC电池常超过computer生存期。

可以用RTC完成以下功能:(1)读或设置绝对时钟,并在clock updates时产生中断;(2)以2HZ到8192HZ来产生周期性中断;(3)设置alarms。

jiffies仅是相对于系统启动的相对时间,如果想获取absolute time或wall time,则需要使用RTC,内核用变量xtime来记录,当系统启动时,读取RTC并记录在xtime中,当系统halt时,则将wall time写回RTC,函数do_gettimeofday()来读取wall time。

#include <linux/time.h>static struct timeval curr_time;do_gettimeofday(&curr_time);my_timestamp = cpu_to_le32(curr__sec); /* Record timestamp */ 用户空间获取wall time的函数:time()返回calendar time或从00:00:00 on January 1,1970的秒数;(2)localtime():返回calendar time in broken-down format;(3)mktime():与localtime()相反;(4)gettimeofday()以microsecond 精确度返回calendar时间。

相关主题