一.度量時間差
時鐘中斷是由系統的定時硬件以週期性的時間間隔產生,這個間隔(即頻率)由內核根據HZ來確定,HZ是一個與體系結構無關的常量(定義在),可配置(50-1200),在X86平臺,默認值爲1000.HZ的含義是系統每秒鐘產生的時鐘中斷的次數.
每當時鐘中斷髮生時,全局變量jiffies(一個32位的unsigned long 變量,定義在)就加1,因此jiffies記錄了字linux系統啓動後時鐘中斷髮生的次數.驅動程序常利用jiffies來計算不同事件間的時間間隔.
內核提供了一組宏用來比較時間量:
#include
int time_after(unsigned long a, unsigned long b);
int time_before(unsigned long a, unsigned long b);
int time_after_eq(unsigned long a, unsigned long b);
int time_after_eq(unsigned long a, unsigned long b);
這幾個宏可以理解爲 a 宏名 b? 1:0.
獲取當前時間:
#include
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
void do_gettimeofday(struct timeval *tv)
二.內核定時器
內核定時器用於控制某個函數(定時器處理函數)在未來的某個特定時間執行.內核定時器註冊的處理函數只執行一次.處理過後即失效.
當內核定時器被調度運行時,幾乎可以肯定其不會在註冊這些函數的進程正在執行時.相反,它會異步的執行.這種異步類似於硬件中斷髮生時的情景.實際上,內核定時器是被"軟件中斷"調度運行的.因此,其運行於原子上下文中.這點和tasklet很類似.處於原子上下文的進程有一些運行時的限制:
1. 不能訪問用戶空間.因爲沒有進程上下文.無法與特定的進程與用戶關聯
2. 不能執行調度或休眠.
3. Current指針在原子模式下無意義.
內核定時器被組織成雙向鏈表,使用struct timer_list結構描述.
struct time_list{
unsigned long expires; //超時的jiffies值
void(*function)(unsigned long) ; //註冊的定時器處理函數
unsigned long data; //定時器處理函數的參數
}
這3個字段表示,當jiffies等於expires時,內核會調度function函數運行.data是傳遞給function的參數的指針,如果function函數需要不止一個參數,那麼可以將這幾個參數組成一個結構體,並將結構體的指針賦值給data.
三.管理定時器的接口
void init_timer(struct time_list *timer);
初始化定時器隊列結構.timer_list結構在使用前必須初始化,這是要保證結構體中其他的成員能正確賦值.
void add_timer(struct time_list *timer);
啓動定時器.
int del_timer(struct time_list *timer);
在定時器超時前將定時器刪除.當定時器超時後,系統會自動將其刪除.
四.內核定時器的使用方法
在使用 struct timer_list 之前,需要初始化該數據結構,確保所有的字段都被正確地設置。初始化有兩種方法。
方法一:
DEFINE_TIMER(timer_name, function_name, expires_value, data);
該宏會靜態創建一個名叫 timer_name 內核定時器,並初始化其 function, expires, name 和 base 字段。
方法二:
struct timer_list mytimer;
setup_timer(&mytimer, (*function)(unsigned long), unsigned long data);
mytimer.expires = jiffies + 5*HZ;
方法3:
struct timer_list mytimer;
init_timer(&mytimer);
mytimer ->timer.expires = jiffies + 5*HZ;
mytimer ->timer.data = (unsigned long) dev;
mytimer ->timer.function = &corkscrew_timer; /* timer handler */
通過init_timer()動態地定義一個定時器,此後,將處理函數的地址和參數綁定給一個timer_list,
注意,無論用哪種方法初始化,其本質都只是給字段賦值,所以只要在運行 add_timer() 之前,expires, function 和 data 字段都可以直接再修改。
關於上面這些宏和函數的定義,參見 include/linux/timer.h。
註冊
定時器要生效,還必須被連接到內核專門的鏈表中,這可以通過 add_timer(struct timer_list *timer) 來實現。
重新註冊
要修改一個定時器的調度時間,可以通過調用 mod_timer(struct timer_list *timer, unsigned long expires)。mod_timer() 會重新註冊定時器到內核,而不管定時器函數是否被運行過。
註銷
註銷一個定時器,可以通過 del_timer(struct timer_list *timer) 或 del_timer_sync(struct timer_list *timer)。其中 del_timer_sync 是用在 SMP 系統上的(在非SMP系統上,它等於del_timer),當要被註銷的定時器函數正在另一個 cpu 上運行時,del_timer_sync() 會等待其運行完,所以這個函數會休眠。另外還應避免它和被調度的函數爭用同一個鎖。對於一個已經被運行過且沒有重新註冊自己的定時器而言,註銷函數其實也沒什麼事可做。
int timer_pending(const struct timer_list *timer)
這個函數用來判斷一個定時器是否被添加到了內核鏈表中以等待被調度運行。注意,當一個定時器函數即將要被運行前,內核會把相應的定時器從內核鏈表中刪除(相當於註銷)
當刪除定時器時,必須小心一個潛在的競爭條件。當del_timer()返回後,可以保證的只是: 定時器不會再被激活(也就是,將來不會執行),但是在多處理機器上定時器中斷可能已經在其他處理器上運行了,所以刪除定時器時需要等待可能在其他處理器上 運行的定時器處理程序都退出,這時就要使用del_timer_sync()函數執行刪除工作:
del_timer_sync(&my_timer);
和del_timer()函數不同,del_timer_sync()函數不能在中斷上下文中使用.
特別注意:
特別要注意順序:提供兩個順序參考:
一:
- struct timer_list mytimer;
- init_timer(&mytimer);
- mytimer.function=mytimer_function;
- mytimer.expires=jiffies+HZ/100
- add_timer(&mytimer);
mytimer.data的值可以在第五條之前設置,也可以不設置;看自己用不用傳遞參數。 注意jiffies這個的數值是一直在變的所以第四天和第五條不要離太遠!!!!
再給一個順序
二:
- struct timer_list mytimer;
- init_timer(&mytimer);
- mytimer.function=mytimer_function;
- add_timer(&mytimer); //沒有設置expires字段。默認爲0,立即執行mytimer_function;
- 。。。。。。
- 。。。。。。
- mod_timer(&mytimer, jiffies+HZ/100); //設定expires字段。重啓定時器;