jiffies的回绕问题及解决方案

来源:互联网 发布:亚马逊广告优化 编辑:程序博客网 时间:2024/05/16 15:25

一、什么是jiffies

全局变量jiffies用来记录自启动以来产生的节拍的总数。系统启动时会将该变量初始化为0,此后,每当时钟中断产生时就会增加该变量的值。jiffies和另外一个变量息息相关:HZ。HZ是每秒系统产生的时钟中断次数,所以jiffies每秒增加的值也就是HZ。

jiffies定义于

extern unsigned long volatile jiffies;

从上面的代码中我们可以看出,jiffies被声明为无符号的长整形,在32位的机器上能用的最大值是2^32-1,在64位的机器上能用的最大值就是2^64-1。

二、jiffies回绕问题

首先,我们来看一下什么是回绕。正如我们所说,jiffies有自己的最大值,我们假设机器是32位的,此时jiffies为2^32-1,那么按照理想情况下一次加1后jiffies的值应该是2^32,但是学过C语言或者是C++语言的同学都知道,如果一个数加到了它的最大值,在增加时就会回到最小值,这就是回绕。

根据HZ与jiffies的关系,在32位的机器上,如果系统的HZ值为100的情况下,jiffies的回绕时间大约为500天左右,如果HZ为1000,那么回绕时间将只有50天左右。

当发生回绕时,会使原来正常的程序产生不正常的结果,例如下面的例子。

unsigned long timeout = jiffies + HZ/2; //0.5后超时/*执行一些任务*/......../*然后检查时间是否过长*/if(timeout>jiffies){  /*没有超时...*/}else{  /*超时了....*/}

使用timeout来检测程序的执行是否超时,timeout被设为jiffies加上0.5秒,程序执行一些步骤后检测timeout和jiffies的大小,如果timeout大于jiffies,说明上面这些步骤是在0.5秒内完成的。但是当jiffies出现回环时,上面的检测就会出现问题,因为jiffies回环后会变成一个非常小的数,所以timeout肯定会大于jiffies,但是程序却可能会执行超过0.5秒。

三、如何解决回绕问题

为了解决回绕问题,linux中定义了以下四个的比较函数,其中unknown参数一般为jiffies,know参数为要比较的对象,例如上文中timeout。

#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)

这四个宏仅仅是将jiffies和要比较的数值从unsigned long变为了long为什么就能避免回绕了呢。

为了便于说明,我们假设jiffies是单字节的无符号数,范围为0~255。假如jiffies开始为250,由于是无符号数据,那么它在机器中实际存储的补码为11111010,记为J1;timeout如果被设为252,实际存储为11111100;而过了一会jiffies发生回绕编变成了1,实际存储变为00000001,记为J2。 那么此时如果按照无符号数比较其大小关系,有: J1<timeout & J2 <timeout,这样的结果与实际的时间节拍统计是不符的,但是如果我们按照有符号数来比较会有什么结果呢?
J1如果按照有符号数读取,首先从补码转换成原码:10000110,转换成十进制为-6;
timeout按照有符号数读取,首先从补码转换成原码:10000100,转换成十进制为-4;
J2按照有符号数读取,首先从补码转换成原码:00000001,转换成十进制为1;
这样它们的大小关系为:J1<timeout<J2 。 这与实际的节拍计数就吻合了,以上内核定义的几个宏就是通过这种方式巧妙解决jiffies回绕问题的。

3 0
原创粉丝点击