歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核實踐之工作隊列

Linux內核實踐之工作隊列

日期:2017/3/1 10:25:36   编辑:Linux內核

工作隊列(work queue)是另外一種將工作推後執行的形式,它和tasklet有所不同。工作隊列可以把工作推後,交由一個內核線程去執行,也就是說,這個下半部分可以在進程上下文中執行。這樣,通過工作隊列執行的代碼能占盡進程上下文的所有優勢。最重要的就是工作隊列允許被重新調度甚至是睡眠。

那麼,什麼情況下使用工作隊列,什麼情況下使用tasklet。如果推後執行的任務需要睡眠,那麼就選擇工作隊列。如果推後執行的任務不需要睡眠,那麼就選擇tasklet。另外,如果需要用一個可以重新調度的實體來執行你的下半部處理,也應該使用工作隊列。它是唯一能在進程上下文運行的下半部實現的機制,也只有它才可以睡眠。這意味著在需要獲得大量的內存時、在需要獲取信號量時,在需要執行阻塞式的I/O操作時,它都會非常有用。如果不需要用一個內核線程來推後執行工作,那麼就考慮使用tasklet。

1. 工作、工作隊列和工作者線程

如前所述,我們把推後執行的任務叫做工作(work),描述它的數據結構為work_struct,這些工作以隊列結構組織成工作隊列(workqueue),其數據結構為workqueue_struct,而工作線程就是負責執行工作隊列中的工作。系統默認的工作者線程為events,自己也可以創建自己的工作者線程。

2.表示工作的數據結構

工作用<linux/workqueue.h>中定義的work_struct結構表示:

struct work_struct {

atomic_long_t data;

#define WORK_STRUCT_PENDING 0 /* T if work item pending execution */

#define WORK_STRUCT_FLAG_MASK (3UL)

#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)

struct list_head entry;

work_func_t func;

#ifdef CONFIG_LOCKDEP

struct lockdep_map lockdep_map;

#endif

};

這些結構被連接成鏈表。當一個工作者線程被喚醒時,它會執行它的鏈表上的所有工作。工作被執行完畢,它就將相應的work_struct對象從鏈表上移去。當鏈表上不再有對象的時候,它就會繼續休眠。

3.創建推後的工作

要使用工作隊列,首先要做的是創建一些需要推後完成的工作。可以通過DECLARE_WORK在編譯時靜態地建該結構:

DECLARE_WORK(name, void (*func) (void*), void *data);

這樣就會靜態地創建一個名為name,待執行函數為func,參數為data的work_struct結構。

同樣,也可以在運行時通過指針創建一個工作:

INIT_WORK(struct work_struct *work, woid(*func) (void *), void *data);

這會動態地初始化一個由work指向的工作。

4. 工作隊列中待執行的函數

工作隊列待執行的函數原型是:

void work_handler(void*data)

這個函數會由一個工作者線程執行,因此,函數會運行在進程上下文中。默認情況下,允許響應中斷,並且不持有任何鎖。如果需要,函數可以睡眠。需要注意的是,盡管該函數運行在進程上下文中,但它不能訪問用戶空間,因為內核線程在用戶空間沒有相關的內存映射。通常在系統調用發生時,內核會代表用戶空間的進程運行,此時它才能訪問用戶空間,也只有在此時它才會映射用戶空間的內存。

5. 對工作進行調度

現在工作已經被創建,我們可以調度它了。想要把給定工作的待處理函數提交給缺省的events工作線程,只需調用

schedule_work(&work);

work馬上就會被調度,一旦其所在的處理器上的工作者線程被喚醒,它就會被執行。

有時候並不希望工作馬上就被執行,而是希望它經過一段延遲以後再執行。在這種情況下,可以調度它在指定的時間執行:

schedule_delayed_work(&work,delay);

這時,&work指向的work_struct直到delay指定的時鐘節拍用完以後才會執行。

Copyright © Linux教程網 All Rights Reserved