系統定時器頻率(節拍率)是通過靜態預處理器定義的,也就是HZ,編寫內核代碼時,不要以為HZ值是固定不變的值。連續兩次時鐘的間隔時間叫做節拍,它等於節拍率分之一秒。高HZ有利於提供諸如poll和select函數運行的精度;有利於提高進程搶占的准確度;有利於獲得更精細的解析度。但是高HZ也會減少處理器處理其他工作的時間,而且還會更頻繁的打亂處理器高速緩存並增加耗電。
全局變量Jiffies用來記錄自系統啟動以來產生的節拍的總數,Jiffies一秒內增加的值就是HZ,系統運行時間以秒為單位計算,就等於jiffies/HZ。
extern unsigned long volatile jiffies; //定義jiffies
unsigned long later=jiffies+5*HZ //從現在開始5秒
在上面jiffies定義中,需要說明幾點。其一,jiffies類型為unsigned long,其他類型是不對的,內核時間管理代碼使用整個64位的jiffies_64,以此避免溢出,而jiffies僅是讀取jiffies_64的低32位。其二,C編譯器通常只將變量裝在一次,一般情況下不能保證循環中的jiffies變量在每次循環中被讀取時都重新被載入,所以關鍵字volatile指示編譯器在每次訪問變量時都重新從主內存中獲得,而不是通過寄存器中的變量別名來訪問。
如果節拍數達到最大值後還要繼續增加的話,它的值會回繞到0,這就叫jiffies的回繞。內核提供四種宏來解決jiffies回繞問題,time_after,time_befiore,time_after_eq,time_befiore_eq。舉例如下
unsigned long time=jiffies+HZ; //一秒後超時
if(time_before(jiffies,timeout)){
//沒有超時的處理
}else{
//超時了的處理
}
體系結構提供兩種設備進行計時,系統定時器和實時時鐘。系統定時器提供一種周期性觸發中斷機制,實時時鐘最主要的作用是在啟動時初始化牆上時間(當前實際時間)xtime變量。
動態定時器
Struct time_list my_timer ; //創建定時器
Init_timer(&my_timer); //初始化定時器
my_timer.expires=jiffies+delay; //填充超時時間
my_timer.data=0; //填充超時處理函數void my_funciton(unsigned long data)形參
my_timer.function=my_function; //填充超時處理函數
add_timer(&my_timer); //激活定時器
mod_timer(&my_timer,jiffies+new_delay);//修改新的定時時間並激活
del_timer_sync(&my_timer); //刪除定時
首先需要說明下,驅動中我們常用setup_time函數代替創建,初始化,填充代碼。其次,動態定時器並不是周期運行,它在超時後就自動撤銷,所以不需要為已經超時的定時器調用刪除函數。另外,刪除定時器時需要等待可能再其他處理器上運行的定時器處理程序都退出,所以要使用del_timer_sync,而del_timer只能保證定時器不再被激活。同時,這種定時器不能完全保證實時,所以不能用這種定時器來實現任何硬實時任務。
其他延時策略
1. 忙等待:使用time_befoer等。延遲執行,都不應該在持有鎖時或者禁止中斷時發生。
2. 短延時:使用udelay、ndelay或mdelay。udelay函數應當只在小延時中調用,通常超過1ms的范圍不要使用udelay延時。對於較長延時,mdelay工作良好。
3. 使用schedule_timeout()函數,由於此函數需要調用調度程序,所以這個函數使用時必須處於進程上下文,並且不能持有鎖。有的時候等待隊列上的某個任務可能既在等待一個特定事件的到來,又在等待一個特定事件的到期,這種情況下,代碼可以使用schedule_timeout()函數代替schedule ()函數,這樣一來,當希望的指定時間到期,任務就會被喚醒。