一、幾個概念
1、節拍率:HZ
系統定時器頻率(節拍率)是通過靜態預處理定義的,也就是HZ(赫茲),在系統啓動時按照HZ值對硬件進行設置。體系結構或機器不同,HZ值都有可能不同。
內核在<asm/param.h>文件中定義了這個值。
假設一個進程的時間片只剩下2ms了,此時調度程序又要求搶佔該進程,然後去運行一個新進程,然而該搶佔不會在下一個時鐘中斷到來前發生。對於頻率爲100HZ的時鐘來說,最壞要在10ms後,當下一個時鐘中斷到來時才能進行搶佔。
2、jiffies
全局變量jiffies用來記錄自系統啓動以來產生的節拍的總數。啓動時,內核將改變量初始化爲0,此後,每次時鐘中斷處理程序就會增加該變量值,因爲一秒內時鐘中斷次數等於HZ,所以jiffies一秒內增加的值也就爲HZ。系統運行的時間以秒爲單位計算,就等於jiffies/HZ。
jiffies定義於文件<linux/jiffies.h>中:
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
3、jiffies的迴繞
對於32位無符號長整形,最大取值爲2的32次方減1,所以在溢出前,定時器節拍計數最大爲4294967295,如果節拍計數達到了最大計數後還要繼續增加的話,它的值會迴繞(wrap around)到0。
在<linux/jiffies.h>中定義了幾個宏解決迴繞的問題:
#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)
二、內核定時器
1、使用定時器
定時器由結構timer_list表示,定義在頭文件<linux/timer.h>
1 struct timer_list {
2 /*
3 * All fields that change during normal runtime grouped to the
4 * same cacheline
5 */
6 struct list_head entry;
7 unsigned long expires;
8 struct tvec_base *base;
9
10 void (*function)(unsigned long);
11 unsigned long data;
12
13 int slack;
14
15 #ifdef CONFIG_TIMER_STATS
16 int start_pid;
17 void *start_site;
18 char start_comm[16];
19 #endif
20 #ifdef CONFIG_LOCKDEP
21 struct lockdep_map lockdep_map;
22 #endif
23 };
使用定時器不需要深入瞭解該數據結構,內核提供了一組接口來簡化操作。所有這些接口都聲明在文件<linux/timer.h>中,大多數接口在文件kernel/timer.c中獲得實現。
創建定時器時需要先定義它:
struct timer_list my_timer;
初始化:
init_timer(&my_timer);
填充結構體:
my_timer.expires = jiffies + delay; //定時器超時時的節拍數
my_timer.data = 0; //給定時器處理函數傳入0值
my_timer.function = my_function; //定時器超時時調用的函數
my_timer.expires表示超時時間,它是以節拍爲單位的絕對計數值。如果當前jiffies計數等於或大於my_timer.expires,那麼my_timer.function指向的處理函數就會開始執行,另外該函數還要使用長整型參數my_timer.data,data參數的使用可以利用同一個處理函數註冊多個定時器,只需通過該參數就能區別對待他們。下面是它的函數原型。
void my_function(unsigned long data);
激活定時器:
add_timer(&my_timer);
定時器也有可能延誤到下一個時鐘節拍才能運行,所以不能用定時器來實現任何硬實時任務。
下面的函數mod_timer用來修改定時器超時時間。mod_timer也可操作那些已經初始化,但是還沒被激活的定時器,如果定時器沒被激活mod_timer會激活它,如果調用的定時器未被激活,該函數返回0,否則返回1。
mod_timer(&my_timer, jiffies + new_delay);
如果需要在定時器超時前停止定時器,可以使用del_timer()函數:
如果定時器還未被激活該函數返回0,否則返回1。不需要爲已經超時的定時器使用該函數,因爲它們會自動刪除。
del_timer(&my_timer);
當刪除定時器時,必須注意一個潛在的競爭條件,當del_timer()返回後,可以保證的只有定時器不會再被激活,但是在多處理器機器上定時器中斷可能已經在其他處理器上運行了,所以刪除定時器時需要等待可能在其他處理器上運行的定時器處理程序都退出,這時就要使用del_timer_sync()函數執行刪除工作:
del_timer_sync(&my_timer);
和del_timer()函數不同,del_timer_sync()函數不能在中斷上下文中使用。