什麼是jiffies
在Linux內核中有一個全局變量jiffies,這個變量被定義在<linux/jiffies.h>文件中。這個全局變量是被用來記錄系統啓動以來產生的節拍的總數。在內核啓動的時候,內核將這個變量初始化爲0.此後,每次時鐘中斷時,這個值就會增加1.而每一秒內要中斷的次數是HZ。所以系統的運行時間是jiffies/HZ。
迴繞問題的產生
在Linux系統中,對jiffies有不同的聲明。
#define __jiffy_data __attribute__((section(".data")))
/*
* The 64-bit value is not atomic - you MUST NOT read it
* without sampling the sequence number in jiffies_lock.
* get_jiffies_64() will do this for you as appropriate.
*/
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
對於32位機器使用的聲明是unsigned long volatile __jiffy_data jiffies,這個變量unisigned long,是一個32位的無符號數。而對於64位機器是無符號的64位無符號數。由於jiffies在使用中用的最多的是存放流失的時間,所以只關心底32位。如果要得到jiffies_64可以通過函數:
u64 get_jiffies_64(void);
在64位機器中,jiffies只會訪問jiffies_64的低32位地址空間。
jiffies的迴繞問題
由於jiffies變量是可能發生溢出的,對於32位無符號長整型來說它的最大值爲2^32 - 1,所以jiffies的最大值也就是4294967295.如果這個值再增加的話就會回到0然後一次增加。
如果單純的比較大小是不準確的,如下面代碼:
unisgned long timeout = jiffies + HZ/2; /*0.5秒後超時*/
/*執行任務*/
/*查看時間是否超過*/
if (jiffies < timeout){
/*沒有超時*/
} else {
/*超時了*/
}
jiffies迴繞問題的解決方法
對於jiffies的迴繞問題,Linux內核是通過4個宏定義來解決的:#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)((b) - (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) - (b)) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)
如果現在將代碼修改成下面這樣:
unisgned long timeout = jiffies + HZ/2; /*0.5秒後超時*/
/*執行任務*/
/*查看時間是否超過*/
if (time_before(jiffies , timeout){
/*沒有超時*/
} else {
/*超時了*/
}
現在就不可能再次產生迴繞了,在看上面的宏時我們會發現裏面是用的long,而不是聲明時的unsigned long。這一步就是不會產生迴繞的關鍵步驟。原因如下:
我們都知道long是有符號的數據,它的正數範圍是[0-0x7FFFFFFF],負數是:[0x80000000-0xFFFFFFFF]之間。
(1)如果a和b都是在正數範圍,也就是0~0x7FFFFFFF之間的話,並且a > b,這時,(long)((b) - (a)) 是小於0的。
(2)如果a和b都是負數範圍,a本來是大於b,但是在負數中是a小於b,所以就出現了(long)((b) - (a))還是小於0。
(3)如果a在[0-0x7FFFFFFF],而b在[0x80000000-0xFFFFFFFF],這時(long)((b) - (a))還是小於0。
(4)如果a在[0x80000000-0xFFFFFFFF],a在[0-0x7FFFFFFF]之間。這時(long)((b) - (a))還是會小於0。
通過上面的time_before或者是time_after都是可以繞開jiffies繞回問題的。