内核中用全局变量 jiffies 来记录节拍书,定义在文件 include/linux/jiffies.h 中:
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
jiffies_64 用于 64 位的系统,jiffies 用于 32 位系统。既然是用来计数的变量,就会有溢
出的风险。64 位且不提,32 位的 jiffies 在 100Hz 的系统频率下,500 天左右就会溢出回绕,
在 1000Hz 的频率下 50 天左右就会回绕。回绕会带来什么问题,举个例子:
1. unsigned long time_mark = jiffies + HZ * 5;
2.
3. /* 业务处理 */
4.
5. if(time_mark > jiffies)
6. {
7. /* 未超时 */
8. }
9. else
10. {
11. /* 超时 */
12. }
这个例子中,先设定一个时间 time_mark 为当前节拍数再加上 5 秒的节拍,也就是 5
秒后系统节拍 jiffies 会到达的值。这个示例代码的目的是如果业务处理的时间超过 5 秒就执
行超时的处理。极端情况下,比如此时 jiffies 为 0xFFFFFDA7,time_mark 则等于 0xFFFFFF9B,
如果业务处理时间用了 7 秒,jiffies 会因为超过最大值 0xFFFFFFFF 发生反转,从 0 开始重新
计数变成 0x64,这个值远远小于 time_mark,明明超时了,却进入了未超时的条件。
对此,内核的通过把数据强专无符号数强转成有符号数巧妙的规避了回绕的问题,不用
传统的比较运算符去比较,而使用下面几个宏来代替:
#define time_after(unknown,known) ((long)(known) - (long)(unknown)<0)
#define time_before(unkonwn,known) ((long)(unknown) - (long)(known)<0)
#define time_after_eq(unknown,known) ((long)(unknown) - (long)(known)>=0)
#define time_before_eq(unknown,known) ((long)(known) -(long)(unknown)>=0)
以 time_after_eq 为例,参数 unknow 一般指 jiffies,know 通常是我们设定的时间,如上
面例子中的 time_mark。after_eq 即大于等于,如果 unknow 大于等于 know,即 jiffies 大于
time_mark,则返回真,对于上面的例程来说就是超时了。
7.1.3 内核定时器及相关函数
Linux 内核中定时器用数据结构 timer_list 来表示。定义在 include/linux/timer.h 中:
1. struct timer_list {
2. /*
3. * All fields that change during normal runtime grouped to the
4. * same cacheline
5. */
评论0