Linux workqueue_struct 前後版本的比較

Linux自從2.6.20之後,工作隊列發生了一些變化,目前從網絡上搜索的資料一般都是介紹老版本的工作隊列,很少見到對新版本的介紹。本文對新老版本都做了簡要概述,並分別提供了簡單的實作案例。 工作隊列(work queue)是Linux kernel中將工作推後執行的一種機制。這種機制和BH或Tasklets不同之處在於工作隊列是把推後的工作交由一個內核線程去執行,因此工作隊列的優勢就在於它允許重新調度甚至睡眠。 工作隊列是2.6內核開始引入的機制,在2.6.20之後,工作隊列的數據結構發生了一些變化,因此本文分成兩個部分對2.6.20之前和之後的版本分別做介紹。 1、2.6.0~2.6.19 數據結構: struct work_struct { unsigned long pending; struct list_head entry; void (*func)(void *); void *data; void *wq_data; struct timer_list timer; }; pending是用來記錄工作是否已經掛在隊列上; entry是循環鏈表結構; func作爲函數指針,由用戶實現; data用來存儲用戶的私人數據,此數據即是func的參數; wq_data一般用來指向工作者線程(工作者線程參考下文); timer是推後執行的定時器。 work_struct的這些變量裏,func和data是用戶使用的,其他是內部變量,我們可以不用太過關心。 API: 1) INIT_WORK(_work, _func, _data) 初始化指定工作,目的是把用戶指定的函數_func及_func需要的參數_data賦給work_struct的func及data變量。 2) int schedule_work(struct work_struct *work) 對工作進行調度,即把給定工作的處理函數提交給缺省 的工作隊列和工作者線程。工作者線程本質上是一個普通的內核線程,在默認情況下,每個CPU均有一個類型爲“events”的工作者線程,當調用 schedule_work時,這個工作者線程會被喚醒去執行工作鏈表上的所有工作。 3) int schedule_delayed_work(struct work_struct *work, unsigned long delay) 延遲執行工作,與schedule_work類似。 4) void flush_scheduled_work(void) 刷新缺省工作隊列。此函數會一直等待,直到隊列中的所有工作都被執行。 5) int cancel_delayed_work(struct work_struct *work) flush_scheduled_work並不取消任何延遲執行的工作,因此,如果要取消延遲工作,應該調用cancel_delayed_work。 以上均是採用缺省工作者線程來實現工作隊列,其優點是簡單易用,缺點是如果缺省工作隊列負載太重,執行效率會很低,這就需要我們創建自己的工作者線程和工作隊列。 API: 1) struct workqueue_struct *create_workqueue(const char *name) 創建新的工作隊列和相應的工作者線程,name用於該內核線程的命名。 2) int queue_work(struct workqueue_struct *wq, struct work_struct *work) 類似於schedule_work,區別在於queue_work把給定工作提交給創建的工作隊列wq而不是缺省隊列。 3) int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay) 延遲執行工作。 4) void flush_workqueue(struct workqueue_struct *wq) 刷新指定工作隊列。 5) void destroy_workqueue(struct workqueue_struct *wq) 釋放創建的工作隊列。 下面一段代碼可以看作一個簡單的實作: void my_func(void *data) { char *name = (char *)data; printk(KERN_INFO “Hello world, my name is %s!/n”, name); } struct workqueue_struct *my_wq = create_workqueue(“my wq”); struct work_struct my_work; INIT_WORK(&my_work, my_func, “Jack”); queue_work(my_wq, &my_work); destroy_workqueue(my_wq); 2、2.6.20~2.6.?? 自2.6.20起,工作隊列的數據結構發生了一些變化,使用時不能沿用舊的方法。 數據結構: typedef void (*work_func_t)(struct work_struct *work); struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; }; 與2.6.19之前的版本相比,work_struct瘦身不少。粗粗一看,entry和之前的版本相同,func和data發生了變化,另外並無其他的變量。 entry我們不去過問,這個和以前的版本完全相 同。data的類型是atomic_long_t,這個類型從字面上看可以知道是一個原子類型。第一次看到這個變量時,很容易誤認爲和以前的data是同 樣的用法,只不過類型變了而已,其實不然,這裏的data是之前版本的pending和wq_data的複合體,起到了以前的pending和 wq_data的作用。 func的參數是一個work_struct指針,指向的數據就是定義func的work_struct。 看到這裏,會有兩個疑問,第一,如何把用戶的數據作爲參數傳遞給func呢?以前有void *data來作爲參數,現在好像完全沒有辦法做到;第二,如何實現延遲工作?目前版本的work_struct並沒有定義timer。 解決第一個問題,需要換一種思路。2.6.20版本之後使用工作隊列需要把work_struct定義在用戶的數據結構中,然後通過container_of來得到用戶數據。具體用法可以參考稍後的實作。 對於第二個問題,新的工作隊列把timer拿掉的用 意是使得work_struct更加單純。首先回憶一下之前版本,只有在需要延遲執行工作時纔會用到timer,普通情況下timer是沒有意義的,所以 之前的做法在一定程度上有些浪費資源。所以新版本中,將timer從work_struct中拿掉,然後又定義了一個新的結構delayed_work用 於處理延遲執行: struct delayed_work { struct work_struct work; struct timer_list timer; }; 下面把API羅列一下,每個函數的解釋可參考之前版本的介紹或者之後的實作: 1) INIT_WORK(struct work_struct *work, work_func_t func) 2) INIT_DELAYED_WORK(struct delayed_work *work, work_func_t func) 3) int schedule_work(struct work_struct *work) 4) int schedule_delayed_work(struct delayed_work *work, unsigned long delay) 5) struct workqueue_struct *create_workqueue(const char *name) 6) int queue_work(struct workqueue_struct *wq, struct work_struct *work) 7) int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay) 8) void flush_scheduled_work(void) 9) void flush_workqueue(struct workqueue_struct *wq) 10) int cancel_delayed_work(struct delayed_work *work) 11) void destroy_workqueue(struct workqueue_struct *wq) 其中,1), 2), 4) ,7)和以前略有區別,其他用法完全一樣。 實作: struct my_struct_t { char *name; struct work_struct my_work; }; void my_func(struct work_struct *work) { struct my_struct_t *my_name = container_of(work, struct my_struct_t, my_work); printk(KERN_INFO “Hello world, my name is %s!/n”, my_name->name); } struct workqueue_struct *my_wq = create_workqueue(“my wq”); struct my_struct_t my_name; my_name.name = “Jack”; INIT_WORK(&(my_name.my_work), my_func); queue_work(my_wq, &my_work); destroy_workqueue(my_wq);

發佈了26 篇原創文章 · 獲贊 4 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章