10.5.1 核心定時器程式設計
軟體意義上的定時器最終依賴硬體定時器來實現,核心在時鐘中斷發生後檢測各定時器是否到期,到期後的定時器處理函式將作為軟中斷在底半部執行。實質上,時鐘中斷處理程式執行 update_process_timers()函式, 該函式呼叫run_local_timers()函式,這個函式處理 timer_softirq 軟中斷,執行當預處理器上到期的所有定時器。
在 linux 裝置驅動程式設計中,可以利用 linux 核心中提供的一組函式和資料結構來完成定時觸發工作或者完成某週期性的事務。 這組函式和資料結構使得驅動工程師多數情況下不用關心具體的軟體定時器究竟對應著怎樣的核心和硬體行為。
linux 核心所提供的用於操作定時器的資料結構和函式如下。
1.timer_list
在 linux 核心中,timer_list 結構體的乙個例項對應乙個定時器,如**清單 10.9所示。
碼清單 10.9 timer_list 結構體
struct timer_list ;
當定時器期滿後, 其中第 5 行的 function()成員將被執行, 而第 4 行的 data 成員則是傳入其中的引數,第 3 行的 expires 則是定時器到期的時間(jiffies)。
如下**定義乙個名為 my_timer 的定時器:
struct timer_list my_timer;
2.初始化定時器
void init_timer(struct timer_list * timer);
上述 init_timer()函式初始化 timer_list 的 entry 的 next 為 null,並給 base 指標賦值。
timer_initializer(_function, _expires, _data)巨集用於賦值定時器結構體的function、expires、data 和 base 成員,這個巨集的定義如下所示:
#define timer_initializer(_function, _expires, _data)
define_timer(_ name , _function , _expires , _data)巨集是定義並初始化定時器成員的「快捷方式」 ,這個巨集定義如下所示:
#define define_timer(_name, _function, _expires, _data) \
struct timer_list _name= \
timer_initializer(_function, _expires, _data)
此外,setup_timer()也可用於初始化定時器並賦值其成員,其源**如下:
3.增加定時器static inline void setup_timer(struct timer_list * timer, void (*function)(unsignedlong), unsigned long data)
void add_timer(struct timer_list * timer);
上述函式用於註冊核心定時器,將定時器加入到核心動態定時器鍊錶中。
4.刪除定時器
int del_timer(struct timer_list * timer);
上述函式用於刪除定時器。
del_timer_sync()是 del_ti mer()的同步版,主要在多處理器系統中使用,如果編譯核心時不支援 smp,del_timer_sync()和 del_timer()等價。
5.修改定時器的 expire
int mod_timer(struct timer_list *timer, unsigned long expires);
上述函式用於修改定時器的到期時間,在新的被傳入的 expires 到來後才會執行定時器函式。
**清單 10.10 給出了乙個完整的核心定時器使用模板,大多數情況下,裝置驅動都如這個模板那樣使用定時器。
**清單 10.10 核心定時器使用模板
從**清單第 19、 40 行可以看出, 定時器的到期時間往往是在目前 jiffies 的基礎上新增乙個時延,若為 hz,則表示延遲 1s。在定時器處理函式中,在做完相應的工作後,往往會延後 expires 並將定時器再次新增到核心定時器鍊錶,以便定時器能再次被觸發。/**** 裝置結體*/
struct ***_dev
; /**** 驅動中的某函式*/
***_func1(...)
/**** 驅動中的某函式*/
***_func2(…)
/*定時器處理函式*/
static void ***_do_timer(unsigned long arg)
10.5.2 例項:秒字元裝置
下面我們編寫乙個字元裝置「second」 (即「秒」 )的驅動,它在被開啟的時候初始化乙個定時器並將其新增到核心定時器鍊錶,每秒輸出一次當前的 jiffies(為此,定時器處理函式中每次都要修改新的 expires),整個程式如**清單 10.11。
**清單 10.1 1 使用核心定時器的 second 字元裝置驅動
#include ...
#define second_major 252 /*預設的 second 的主裝置號*/
static int second_major = second_major;
/*second 裝置結體*/
struct second_dev
;struct second_dev *second_devp; /*裝置結構體指標*/
/*定時器處理函式*/
static void second_timer_handle(unsigned long arg)
/*檔案開啟函式*/
int second_open(struct inode *inode, struct file *filp)
/*檔案釋放函式*/
int second_release(struct inode *inode,struct file *filp)
/*globalfifo 讀函式*/
static ssize_t second_read(struct file *filp, char _ _user *buf, size_t count,
loff_t *ppos)
/*檔案操作結體*/
static const struct file_operations second_fops =
;/*初始化並註冊 cdev*/
static void second_setup_cdev(struct second_dev *dev, int index)
/*裝置驅動模組載入函式*/
int second_init(void)
if (ret < 0)
return ret;
/* 動態申請裝置結體的記憶體*/
second_devp = kmalloc(sizeof(struct second_dev), gfp_kernel);
if (!second_devp) /*申請失敗*/
memset(second_devp, 0, sizeof(struct second_dev));
second_setup_cdev(second_devp, 0);
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
}/*模組解除安裝函式*/
void second_exit(void)
module_author("song baohua");
module_license("dual bsd/gpl");
module_param(second_major, int,s_irugo);
module_init(second_init);
module_exit(second_exit);
在 second 的 open()函式中,將啟動定時器,此後每 1s 會再次執行定時器處理函式,在 second 的 release()函式中,定時器被刪除。
second_dev 結構體中的原子變數 counter 用於秒計數,每次在定時器處理函式中將被 atomic_inc()呼叫原子的增 1,second 的 read()函式會將這個值返回給使用者空間。
《Linux裝置驅動開發詳解》 核心延時
10.6.1 短延遲 linux 核心中提供了如下 3 個函式分別進行納秒 微秒和毫秒延遲。void ndelay unsigned long nsecs void udelay unsigned long usecs void mdelay unsigned long msecs 上述延遲的實現原...
Linux裝置驅動 核心開發
linux裝置驅動需要使用核心api來實現,一般被包含在linux核心原始碼樹中。驅動可以編譯到核心,隨著核心一起在系統啟動的時候被載入。也可以編譯成核心模組,在系統執行起來之後動態地載入到核心中,使得除錯的時候無需重新編譯核心,也無需重啟系統,很大程度上方便了驅動 的除錯。但並不是只有裝置驅動才能...
linux裝置驅動開發詳解
第四章 linux核心模組 1.linux核心模組的優點 1 模組可以不用編譯linux核心,在開發除錯的時候,通過動態載入命令載入進核心就可以執行,大大提公升了開發除錯效率,同時也控制了linux核心的大小 2 模組一旦被載入,它就和linux核心其他的部分一樣,直接執行。2.linux核心模組基...