Linux內核中定義了jiffies變量來記錄從系統啟動到當前時刻系統時鐘所產生的tick數。jiffies變量是一個無符號整型數值,即unsigned long類型。它的聲明如下(在 include/linux/jiffies.h 中):
extern u64 __jiffy_data jiffies_64;
由此可見,jiffies變量在32位系統中的長度是32位,在64位系統中長度為64位。
在32位系統中,HZ=1000時,jiffies只要約 49.7 天就會發生回繞(溢出),而回繞會給內核時間度量帶來混亂和其他潛在的問題。因此,在Linux2.6內核中引入一個64位的無符號整型變量jiffies_64,內核中計時都是對jiffies_64進行遞增。jiffies_64也在 include/linux/jiffies.h 中有聲明:
extern u64 __jiffy_data jiffies_64;
其中,u64為unsigned long long類型。在 1000HZ 的情況下,該變量運行幾億年都不會發出回繞,從而有效防止了回繞可能引起的問題。這就是定義jiffies_64變量的原因。
那麼,既然在32位機器上也是用64位計時,為什麼不直接把jiffies變量改為u64類型呢?或者說干脆用jiffies_64來代替jiffies呢?根本原因是為了保持兼容性及訪問效率!從兼容性方面來看,大量的驅動程序使用jiffies 變量來進行一些與時間相關的操作,所以內核中需要保留該變量,以免影響系統功能;從訪問效率方面來看,因為在 32 位的系統中訪問 64 位的 jiffies_64 變量需要進行兩次內存訪問,一來訪問速度沒有直接訪問 jiffies 來得快,二來無法保證原子性(在兩次內存訪問中間可能會被中斷,從而造成讀取數據的不正確)。但是當真的需要訪問jiffies_64變量時(一般在驅動程序中很少訪問 jiffies_64,通常只有內核核心代碼才會訪問),內核也提供了 get_jiffies_64() 函數來訪問。該函數采用了加鎖機制(xtime_lock),以防止讀取數據的不正確。
雖然,jiffies和jiffies_64是兩個變量,但它們最終指向相同的地址,只是jiffies取的是jiffies_64變量的低32位。這種效果通過鏈接程序實現的。通過鏈接器(ld)腳本 vmlinux.lds (x86 上位於 arch/x86/kernel下) 可看到:
1 OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") 2 OUTPUT_ARCH(i386) 3 ENTRY(phys_startup_32) 4 jiffies = jiffies_64;
其中最後一條語句的作用是,讓符號jiffies的地址等於符號jiffies_64的地址,即讓jiffies變量占用 jiffies_64 的低 32 位。這裡涉及到鏈接器中的一個重要的概念:
在目標文件內定義的符號可以在鏈接器腳本內賦值,此時該符號應被定義為全局的。每個符號都對應一個地址,在鏈接器中的賦值(=)操作就是更改這個符號對應的地址。
所以,這和 C 語言中的等於(=)是完全不同的概念:C 中是賦值,鏈接器中是改變地址。
另外,jiffies_64 變量會被初始化為 INITIAL_JIFFIES ,該值定義在文件 include/linux/jiffies.h 中:
1 /* 2 * Have the 32 bit jiffies value wrap 5 minutes after boot 3 * so jiffies wrap bugs show up earlier. 4 */ 5 #define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))
這樣,就使得系統在啟動後 5 分鐘時發生 jiffies 回繞。這麼做有利於及早暴露設備驅動程序中可能的 jiffies 回繞導致的邏輯錯誤,方便驅動程序的開發。
參考文獻:
【1】:http://www.groad.net/bbs/thread-3352-1-1.html
【2】:http://bbs.csdn.net/topics/320154818
【3】:《Linux內核設計與實現(原書第3版)》第11章
http://www.bkjia.com/Linuxjc/1194396.html TechArticle