原文如下:

关于jiffies变量: 
    全局变量jiffies用来记录自启动以来产生的节拍的总数。系统启动时会将该变量初始化为0,此后,每当时钟中断产生时就会增加该变量的值。jiffies和另外一个变量息息相关:HZ。HZ是每秒系统产生的时钟中断次数,所以jiffies每秒增加的值也就是HZ;在x86体系结构中,内核版本在2.4以前的值为100,在2.6内核中被定义为1000。  jiffies的定义:
    extern unsigned long volatile jiffies;  //定义于<linux/jiffies.h>
    从定义可以看出,jiffies的类型为unsigned long,在32位体系结构上unsigned long是32位,在64位体系结构上是64位。 在32位体系结构上,在系统的HZ值为100的情况下,jiffies的回绕时间大约为500天左右,如果HZ为1000,那么回绕时间将只有50天左右。如果发生了回绕现象,对内核中直接利用jiffies来比较时间的代码将产生很不利的影响,比如在<<Linux Kernle Developmen>>一书中有一个例子可以说明这个问题:
    unsigned long timeout = jiffies + HZ/2; //0.5后超时
    /*执行一些任务*/
    ........
    /*然后检查时间是否过长*/
    if(timeout>jiffies){
           /*没有超时...*/
    }else{
           /*超时了....*/
    }
    在这个例子中,如果设置了timeout后发生了回绕,那么第一个判断条件将变为真,这与实际情况不符,尽管因为实际的时间比timeout要大,但因为jiffies发生了回绕变成了0,所以jiffies肯定小于timeout的值。  内核也专门针对这种情况提供了四个宏来帮助比较jiffies计数:
    #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)
    这些宏看起来很奇妙,只是将计数值强制转换为long类型然后比较就能避免回绕带来的问题,这是为什么呢?这和计算机中数据存储的形式有关!!
    计算机中数据的存储是按照二进制补码的方式存储的,之所以采用补码的方式存储是因为这样可以简化运算规则和线路设计。另外一个相关的概念就是原码,原码采用一个最高位作为符号位,其余位是数据大小的二进制表示。 补码的定义是这样的:正数的补码即为原码,负数的补码为原码除符号位外其他各位取反再加1。举例如下:
    [+1]补码 = [+1]原码 = 0000 0001
    [- 1]补码 =  [- 1]原码取反+1 = 1111 1110 + 1 = 1111 1111
    而c语言中的数据类型相当于在代码和实际机器的存储之间的一个中间层,机器中存储的数据,如果按照不同的类型格式取读取就会得到不同的结果,才代码和实际存储之间,编译器充当了翻译者的角色。这是编译器能实现多种数据类型和强制类型转换的基础。
    有了这些基础后,就不难理解上述宏定义的巧妙之处了,为了便于说明,以下假设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回绕问题的

最近在慢慢的啃着 Linux 内核的相关源码,读到 jiffies 这里,这个东西和 windows 下 GetTickCount 获得的值是类似的,就是系统启动以来所经历的 tick 数(windows 下是一毫秒一 tick),神马 timeout、delay、sleep 之类的东西还有进程的调度等,凡是与时间有关的东西大多依赖于它。以前看书的时候总是读到类似这样的内容:这个值一般是32位的,会在0到2^32之间循环,约49.71天。

那时还很天真的想,自己一般一天关机一次,想来是够用了,现在再看,够用个毛啊,一年到头不关机的机器多的是啊。想来那些写操作系统的大牛是不会留下这么二的 BUG 的,果然,今天研究到了这里,在 Linux 内核中使用了一个这样的宏来解决这个问题:

#define time_after(a,b)\
(typecheck(unsigned long,a) &&\
typecheck(unsigned long,b) &&\
((long)(b)-(long)(a)<0) //当 a 在 b 的后面(大于等于),此宏为真  

将无符号数转成有符号数计算,的确是可以解决自加溢出的问题,以 char 为例,unsigned char 的 255 比 0 大,但是转成 signed char 之后其值为 -1 比 0 小,于是 -1, 0, 1 ... 就形成了连续性的递增关系,解决了溢出时的问题(这里不明白的同学请随便找一本C语言书看看补码、反码那一块)。

结合程序来看,假设我需要设置一个 5 tick 的延时,那么 timeout = jiffies + 5;如果运气不好,恰巧这个时候 jiffies 等于254,那么 timeout  等于 254 + 5 = 3,若没有使用宏而直接判断 jiffies > timeout ,那么结果就不对了,本来在 5 个 tick 内 jiffies 都应该小于 timeout 才对的,但 254 直接就大于 3,于是延时失败了。而使用宏的话 254 当做 -2 ,于是 5 个 tick 过后它才会大于 3,符合预期。

但我写到这里,上面的内容基本上等同于废话 ^@^ ,因为那些并不是我读到这个部分源码时所纠结的问题,我纠结的是,当 jiffies 从 127 加到 128 ,由正数转为负数的回绕产生时,time_after 是如何保证比较的结果的正确性的?为此,我自己写了个简化版的宏来做试验:

#define time_after(a,b) ((char)(b)-(char)(a)<0)
//这个宏在 VC 下是不对的,后面解释  

经过一番研究以及阅读别人的帖子,我终于了解了它的原理,在此与大家分享,希望能够对他人有所帮助。

我们假设当前的 jiffies 值为 127 其 5 个 tick 之后的值为 132 ,以有符号来解释该数据则 timeout = -124,那么 jiffies 如何可能会小于 timeout 呢?timeout - jiffies > 0 又是怎么成立的呢?请听下回分解!

别砸显示器,开个玩笑。-124 - 127 = -251 是负数,貌似条件无法成立了,但它就能成立,因为 -251 在内存里的存储是 0xFF05,它超出了一个 byte 的范围,于是将其高位截去剩下 0x05,它是正数,于是 timeout - jiffies > 0 就成立了,坑爹啊!!!

但这个写法有一个小小的缺点,就是延时 tick 不能大于 127,否则计算就不正确了,换成 32 位的情况就是延时 tick 不能大于 2^31-1 想来也没有哪个程序会一次性 delay 25 天吧,所以这一套宏便能够胜任了。

顺便一提,我比较倒霉,因为我是在 VC 的环境下测试的这个宏,结果怎么都不对,原因是在于其运算时隐式转换的规则有细微的不同,它得出结果后并没有将数据截去高位,而是应该升级成了 int 型,于是负号被保留了下来,判断出错。改为如下即可:

#define time_after(a,b) ((char)((char)(b)-(char)(a))<0)  

对 jiffies 溢出、回绕及 time_after 宏的理解的更多相关文章

  1. jiffies溢出与时间先后比较-time_after,time_before【转】

    转自:http://www.cnblogs.com/hfyinsdu/p/4600052.html 参考地址: http://blog.csdn.net/jk110333/article/detail ...

  2. jiffies溢出与时间先后比较-time_after,time_before

    参考地址: http://blog.csdn.net/jk110333/article/details/8177285 http://blog.chinaunix.net/uid-23629988-i ...

  3. linux内核中jiffies的回绕问题【转】

    本文转载自:http://blog.csdn.net/yuanlulu/article/details/6019862 ======================================== ...

  4. 关于jiffies回绕以及time_after,time_before

    系统中有非常多变量用来记录一个单调递增的现实,典型的有两个,一个是TCP的序列号.还有一个就是jiffies,可是由于计算机内表示的数字都是有限无界的,所以不论什么数字都不能做到全然意义的单调递增,它 ...

  5. linux内核计算时间差以及jiffies溢出

    jiffies是每一个时钟中断,都会加1.这就导致一个问题.不管jiffies(一般来说是unsigned long类型)多少个字节,总有溢出的时候. 更极端的时候.当期jiffies是0xfffff ...

  6. 使用jiffies的时间比较函数time_after、time_before

    1. jiffies简介 首先,操作系统有个系统专用定时器(system timer),俗称滴答定时器,或者系统心跳. 全局变量jiffies取值为自操作系统启动以来的时钟滴答的数目,在头文件< ...

  7. C 宏定义 理解(1)

  8. jiffies和HZ

    全局变量jiffies用来记录自系统启动以来产生的节拍的总数.启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值.一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的 ...

  9. Linux内核jiffies简介

    在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies,首先看timeval结构struct timeval{time_t tv_sec; ...

随机推荐

  1. reserve的使用

    reserve: 强迫容器将它的容量变成n 可以避免不必要的重新分配 如果n大于当前容量,那么正常. 如果n小于当前容量,vector会忽略,string则是减小为 max(size(),n). 如果 ...

  2. ●POJ 2125 Destroying The Graph

    题链: http://poj.org/problem?id=2125 题解: 最小割 + 输出割方案.建图:拆点,每个题拆为 i 和 i'分别表示其的入点和出点建立超源 S和超汇 T.S -> ...

  3. [bzoj省选十连测推广赛2]T2七彩树

    抄自:http://blog.csdn.net/coldef/article/details/61412577 当时看了就不会,看了别人的题解不懂怎么维护,最后抄了个代码....... 给定一棵n个点 ...

  4. Thinkphp中的A 函数(Thinkphp3.2.3版本)

    A函数是TP中实例化控制器的一个快捷函数,它的语法结构如下: A('模块/控制器') //当控制器层名称不是 Controller 时须这样写 A('模块/控制器','控制器层名称') 如果要调用的控 ...

  5. tensorflow deepmath:基于深度学习的自动化数学定理证明

    Deepmath Deepmath项目旨在改进使用深度学习和其他机器学习技术的自动化定理证明. Deepmath是Google研究与几所大学之间的合作. 免责声明: 该存储库中的源代码不是Google ...

  6. Java JS SHA-256加密

    http://www.cnblogs.com/elaron/archive/2013/04/09/3010375.html js部分: <html> <head></he ...

  7. 美链BEC合约漏洞技术分析

    这两天币圈链圈被美链BEC智能合约的漏洞导致代币价值几乎归零的事件刷遍朋友圈.这篇文章就来分析下BEC智能合约的漏洞 漏洞攻击交易 我们先来还原下攻击交易,这个交易可以在这个链接查询到. 我截图给大家 ...

  8. 开启CSP网页安全政策防止XSS攻击

     一.简介 CSP是网页安全政策(Content Security Policy)的缩写.是一种由开发者定义的安全性政策申明,通过CSP所约束的责任指定可信的内容来源,(内容可以是指脚本.图片.sty ...

  9. C# IE浏览器

    引用Microsoft HTML Object Library 引用 -> com -> Microsoft HTML Object Library 引用后,显示如图 1.判断ie浏览器是 ...

  10. python笔记一(语言简介、解释器、输入输出)

    一.python语言简介 一顿狂吹python目前有多火.多NB,哈哈哈,不过用起来心情确实很舒畅. 解释性语言:缺点,运行速度慢. 二.python解释器 与C.C++.java不同,以上都需要先将 ...