全域變數jiffies用來記錄自系統啟動以來產生的節拍的總數。啟動時,核心將該變數初始化為0,此後,每次時鐘中斷處理常式都會增加該變數的值。一秒內時鐘中斷的次數等於Hz,所以jiffies一秒內增加的值也就是Hz。
系統已耗用時間以秒為單位,等於jiffies/Hz。
注意,jiffies類型為無符號長整型(unsigned long),其他任何類型存放它都不正確。
將以秒為單位的時間轉化為jiffies:
seconds * Hz
將jiffies轉化為以秒為單位的時間:
jiffies / Hz
相比之下,核心中將秒轉換為jiffies用的多些。
jiffies定義於檔案<linux\Jiffies.h>中:
- /*
- * The 64-bit value is not atomic - you MUST NOT read it
- * without sampling the sequence number in xtime_lock.
- * get_jiffies_64() will do this for you as appropriate.
- */
- extern u64 __jiffy_data jiffies_64;
- 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指的是同一個變數。
- #if (BITS_PER_LONG < 64)
- u64 get_jiffies_64(void);
- #else
- static inline u64 get_jiffies_64(void)
- {
- return (u64)jiffies;
- }
- #endif
- 在<Time.c(kernel)>中
- #if (BITS_PER_LONG < 64)
- u64 get_jiffies_64(void)
- {
- unsigned long seq;
- u64 ret;
- do {
- seq = read_seqbegin(&xtime_lock);
- ret = jiffies_64;
- } while (read_seqretry(&xtime_lock, seq));
- return ret;
- }
當jiffies的值超過它的最大存放範圍後就會發生溢出。對於32位無符號長整型,最大取值為(2^32)-1,即429496795。如果節拍計數達到了最大值後還要繼續增加,它的值就會迴繞到0。
核心提供了四個宏來協助比較節拍計數,它們能正確的處理節拍計數迴繞的問題:
- /*
- * 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)
- /* Same as above, but does so with platform independent 64bit types.
- * These must be used when utilizing jiffies_64 (i.e. return value of
- * get_jiffies_64() */
- #define time_after64(a,b) \
- (typecheck(__u64, a) && \
- typecheck(__u64, b) && \
- ((__s64)(b) - (__s64)(a) < 0))
- #define time_before64(a,b) time_after64(b,a)
- #define time_after_eq64(a,b) \
- (typecheck(__u64, a) && \
- typecheck(__u64, b) && \
- ((__s64)(a) - (__s64)(b) >= 0))
- #define time_before_eq64(a,b) time_after_eq64(b,a)
問題提出:
在2.6以前的核心中,如果改變核心中的HZ值會給使用者空間中某些程式造成異常結果。因為核心是以節拍數/秒的形式給使用者空間匯出這個值的,應用程式便依賴這個特定的HZ值。如果在核心中改變了HZ的定義值,就打破了使用者空間的常量關係---使用者空間並不知道新的HZ值。
解決方案:
核心更改所有匯出的jiffies值。核心定義了USER_HZ來代表使用者空間看到的HZ值。在x86體繫結構上,由於HZ值原來一直是100,所以USER_HZ值就定義為100。核心可以使用宏jiffies_to_clock_t()將一個有HZ表示的節拍計數轉換為一個由USER_HZ表示的節拍計數。
- 在<Time.c(kernel)>中
- /*
- * Convert jiffies/jiffies_64 to clock_t and back.
- */
- clock_t jiffies_to_clock_t(long x)
- {
- #if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
- return x / (HZ / USER_HZ);
- #else
- u64 tmp = (u64)x * TICK_NSEC;
- do_div(tmp, (NSEC_PER_SEC / USER_HZ));
- return (long)tmp;
- #endif
- }
- unsigned long clock_t_to_jiffies(unsigned long x)
- {
- #if (HZ % USER_HZ)==0
- if (x >= ~0UL / (HZ / USER_HZ))
- return ~0UL;
- return x * (HZ / USER_HZ);
- #else
- u64 jif;
- /* Don't worry about loss of precision here .. */
- if (x >= ~0UL / HZ * USER_HZ)
- return ~0UL;
- /* .. but do try to contain it here */
- jif = x * (u64) HZ;
- do_div(jif, USER_HZ);
- return jif;
- #endif
- }
- u64 jiffies_64_to_clock_t(u64 x)
- {
- #if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
- do_div(x, HZ / USER_HZ);
- #else
- /*
- * There are better ways that don't overflow early,
- * but even this doesn't overflow in hundreds of years
- * in 64 bits, so..
- */
- x *= TICK_NSEC;
- do_div(x, (NSEC_PER_SEC / USER_HZ));
- #endif
- return x;
- }
-
- 在<Div64.h(include\asm-i385)>中
- /*
- * do_div() is NOT a C function. It wants to return
- * two values (the quotient and the remainder), but
- * since that doesn't work very well in C, what it
- * does is:
- *
- * - modifies the 64-bit dividend _in_place_
- * - returns the 32-bit remainder
- *
- * This ends up being the most efficient "calling
- * convention" on x86.
- */
- #define do_div(n,base) ({ \
- unsigned long __upper, __low, __high, __mod, __base; \
- __base = (base); \
- asm("":"=a" (__low), "=d" (__high):"A" (n)); \
- __upper = __high; \
- if (__high) { \
- __upper = __high % (__base); \
- __high = __high / (__base); \
- } \
- asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \
- asm("":"=A" (n):"a" (__low),"d" (__high)); \
- __mod; \
- })
使用者空間期望HZ=USER_HZ,但是如果它們不相等,則由宏完成轉換。