linux: work工作隊列__linux

來源:互聯網
上載者:User
 

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

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

  I、2.6.0~2.6.19
資料結構: ?

1 2 3 4 5 6 7 8 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 2 3 4 5 INIT_WORK(_work, _func, _data); int schedule_work( struct work_struct *work); int schedule_delayed_work( struct work_struct *work, unsigned long delay); void flush_scheduled_work( void ); int cancel_delayed_work( struct work_struct *work);

1、初始化指定工作,目的是把使用者指定的函數_func及_func需要的參數_data賦給work_struct的func及data變數。

2、對工作進行調度,即把給定工作的處理函數提交給預設的工作隊列和工作者線程。工作者線程本質上是一個普通的核心線程,在預設情況下,每個CPU均有一個類型為“events”的工作者線程,當調用schedule_work時,這個工作者線程會被喚醒去執行工作鏈表上的所有工作。

3、順延強制工作,與schedule_work類似。

4、重新整理預設工作隊列。此函數會一直等待,直到隊列中的所有工作都被執行。

5、flush_scheduled_work並不取消任何順延強制的工作,因此,如果要取消延遲工作,應該調用cancel_delayed_work。

 

以上均是採用預設工作者線程來實現工作隊列,其優點是簡單易用,缺點是如果預設工作隊列負載太重,執行效率會很低,這就需要我們建立自己的工作者線程和工作隊列。

API:

?

1 2 3 4 5 struct workqueue_struct *create_workqueue( const char *name); int queue_work( struct workqueue_struct *wq, struct work_struct *work); int queue_delayed_work( struct workqueue_struct *wq, struct work_struct *work, unsigned long delay); void flush_workqueue( struct workqueue_struct *wq); void destroy_workqueue( struct workqueue_struct *wq);

1、建立新的工作隊列和相應的工作者線程,name用於該核心線程的命名。

2、類似於schedule_work,區別在於queue_work把給定工作提交給建立的工作隊列wq而不是預設隊列。

3、順延強制工作。

4、重新整理指定工作隊列。

5、釋放建立的工作隊列。

 

下面一段代碼可以看作一個簡單的實作: ?

1 2 3 4 5 6 7 8 9 10 11 12 13 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);

  II、2.6.20~2.6.??
自 2.6.20 起,工作隊列的資料結構發生了一些變化,使用時不能沿用舊的方法。

 

資料結構:

?

1 2 3 4 5 6 7 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用於處理順延強制:

?

1 2 3 4 struct delayed_work {      struct work_struct work;      struct timer_list timer; };

 

下面把API羅列一下,每個函數的解釋可參考之前版本的介紹或者之後的實作:

?

1 2 3 4 5 6 7 8

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.