linux中的jiffies变量

来源:互联网 发布:ping网络命令的使用 编辑:程序博客网 时间:2024/06/06 12:31
全局变量jiffies用来记录自系统启动以来产生的节拍的总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的值也就是Hz。

  系统运行时间以秒为单位,等于jiffies/Hz。

注意,jiffies类型为无符号长整型(unsigned long),其他任何类型存放它都不正确。

将以秒为单位的时间转化为jiffies:

seconds * Hz

将jiffies转化为以秒为单位的时间:

jiffies / Hz

相比之下,内核中将秒转换为jiffies用的多些。

  • jiffies的内部表示

  jiffies定义于文件<linux\Jiffies.h>中:

  1. extern u64 __jiffy_data jiffies_64;
  2. extern unsigned long volatile __jiffy_data jiffies;

ld(1)脚本用于连接主内核映像(在x86上位于arch/i386/kernel/vmlinux.lds.S中),然后用jiffies_64变量的初值覆盖jiffies变量。因此jiffies取整个jiffies_64变量的低32位。

 访问jiffies的代码只会读取jiffies_64的低32位,通过get_jiffies_64()函数就可以读取整个64位的值。在64位体系结构上,jiffies_64和jiffies指的是同一个变量。

  1. #if (BITS_PER_LONG < 64)
  2. u64 get_jiffies_64(void);
  3. #else
  4. static inline u64 get_jiffies_64(void)
  5. {
  6. return (u64)jiffies;
  7. }
  8. #endif
  1. 在<Time.c(kernel)>中
  2. #if (BITS_PER_LONG < 64)
  3. u64 get_jiffies_64(void)
  4. {
  5.     unsignedlong seq;
  6.     u64ret;

  7. do {
  8.        seq = read_seqbegin(&xtime_lock);
  9.        ret = jiffies_64;
  10.     } while(read_seqretry(&xtime_lock, seq));
  11. return ret;
  12. }
  • jiffies的回绕wrap around

 当jiffies的值超过它的最大存放范围后就会发生溢出。对于32位无符号长整型,最大取值为(2^32)-1,即429496795。如果节拍计数达到了最大值后还要继续增加,它的值就会回绕到0。

  内核提供了四个宏来帮助比较节拍计数,它们能正确的处理节拍计数回绕的问题:

  1. #definetime_after(a,b)    \
  2.    (typecheck(unsigned long, a) &&\
  3.     typecheck(unsigned long, b) &&\
  4.     ((long)(b) - (long)(a) < 0))
  5. #definetime_before(a,b)   time_after(b,a)

  6. #define time_after_eq(a,b)  \
  7.    (typecheck(unsigned long, a) &&\
  8.     typecheck(unsigned long, b) &&\
  9.     ((long)(a) - (long)(b) >= 0))
  10. #define time_before_eq(a,b) time_after_eq(b,a)

  11. #define time_after64(a,b)  \
  12.    (typecheck(__u64, a) && \
  13.     typecheck(__u64, b) && \
  14.     ((__s64)(b) - (__s64)(a) < 0))
  15. #define time_before64(a,b) time_after64(b,a)

  16. #definetime_after_eq64(a,b)   \
  17.    (typecheck(__u64, a) && \
  18.     typecheck(__u64, b) && \
  19.     ((__s64)(a) - (__s64)(b) >= 0))
  20. #definetime_before_eq64(a,b)  time_after_eq64(b,a)
  • 用户空间和HZ

  问题提出:

 在2.6以前的内核中,如果改变内核中的HZ值会给用户空间中某些程序造成异常结果。因为内核是以节拍数/秒的形式给用户空间导出这个值的,应用程序便依赖这个特定的HZ值。如果在内核中改变了HZ的定义值,就打破了用户空间的常量关系---用户空间并不知道新的HZ值。

  解决方法:

 内核更改所有导出的jiffies值。内核定义了USER_HZ来代表用户空间看到的HZ值。在x86体系结构上,由于HZ值原来一直是100,所以USER_HZ值就定义为100。内核可以使用宏jiffies_to_clock_t()将一个有HZ表示的节拍计数转换为一个由USER_HZ表示的节拍计数。

  1. 在<Time.c(kernel)>中
  2. clock_t jiffies_to_clock_t(long x)
  3. {
  4. #if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
  5. return x / (HZ / USER_HZ);
  6. #else
  7.     u64 tmp= (u64)x * TICK_NSEC;
  8.    do_div(tmp, (NSEC_PER_SEC / USER_HZ));
  9. return (long)tmp;
  10. #endif
  11. }

  12. unsigned long clock_t_to_jiffies(unsigned long x)
  13. {
  14. #if (HZ % USER_HZ)==0
  15. if (x >= ~0UL / (HZ / USER_HZ))
  16. return ~0UL;
  17. return x * (HZ / USER_HZ);
  18. #else
  19.     u64jif;

  20. if (x >= ~0UL / HZ * USER_HZ)
  21. return ~0UL;

  22.     jif = x* (u64) HZ;
  23.    do_div(jif, USER_HZ);
  24. return jif;
  25. #endif
  26. }
  27. u64 jiffies_64_to_clock_t(u64 x)
  28. {
  29. #if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
  30.    do_div(x, HZ / USER_HZ);
  31. #else
  32.     x *=TICK_NSEC;
  33.    do_div(x, (NSEC_PER_SEC / USER_HZ));
  34. #endif
  35. return x;
  36. }

  1. 在<Div64.h(include\asm-i385)>中
  2. #define do_div(n,base) ({ \
  3.     unsignedlong __upper, __low, __high, __mod, __base; \
  4.     __base =(base); \
  5.    asm("":"=a" (__low), "=d" (__high):"A" (n)); \
  6.     __upper= __high; \
  7. if (__high) { \
  8.        __upper = __high % (__base); \
  9.        __high = __high / (__base); \
  10.     } \
  11.    asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0"(__low), "1" (__upper)); \
  12.    asm("":"=A" (n):"a" (__low),"d" (__high)); \
  13.     __mod;\
  14. })

  用户空间期望HZ=USER_HZ,但是如果它们不相等,则由宏完成转换。


转自:http://www.cnblogs.com/simonshi/archive/2010/12/13/1694819.html