天天看點

工作隊列 ( workqueue )

1.  有些時候核心需要一個異步的程序執行上下文,而工作

    隊列(workqueue)可以滿足這種需求。

    工作隊列中的每一個元素都是一個工作項(work item),

    有一個函數與工作項相關,這個函數就是工作項所要處

    理的任務。

    核心中有一個專門的線程——被稱作worker,來依次執行

    工作隊列中的每一個工作項對應的函數,當工作隊列為

    空時,這個worker就變為空閑狀态(idle),當有新的

    工作項加入到工作隊列時,worker又重新開始執行。

2.  在最早的實作中包括兩種實作方式,一種是整個系統隻

    有一個worker(single thread,ST),另一種是每個

    CPU包含一個worker(multiple thread,MT),每一個

    CPU包含一個屬于自己的worker pool 。

    這兩種實作都引起了系統中對于這種異步上下文的競争,

    隻不過是MT方式的競争可能更小一些。

    是以,核心人員對workqueue作了重新實作,新的實作

    被稱作concurrency managed workqueue(cmwq),新的

    實作的特點如下:

    * 與之前的API相容

    * 實作了統一的每CPU worker pool,減少了資源的浪

      費,提高了并發的靈活性

    * 可以自動調節worker pool和并發的級别

3.  為了簡化執行這種異步上下文 ,引入了work item,它

    是一個簡單的結構體,包含一個函數指針,這個指針指

    向的函數就是需要在異步上文中執行的函數。

    當一個核心子系統或者驅動程式需要在異步上下文中執

    行一個函數時,它首先需要建立一個work item結構體,

    然後将這個結構體加入到工作隊列中。

    工作者線程(worker thread)負責從工作對列中取出work

    item并執行,直到工作隊列中的work item為空。工作

    者線程是由worker-pool管理的。

    cmwq的實作在使用者接口(即子系統或者驅動程式的使用)

    和背景支援上(即如何管理worker pool以及處理work 

    item)有差别。

    每一個CPU上都有兩個worker pool,一個是用來處理普

    通的work item,另一個是用來處理高優先級的work item。

    另外還有一些worker pool用來處理未添加到綁定到CPU

    的wq上的work item,這些worker pool的數量是動态的。

    可以通過修改工作隊列的屬性來改變添加到其上的work 

    item的執行行為,例如在哪個CPU上執行、并發限制、優

    先級等。

    當一個work item添加到workqueue時,根據函數的參數

    以及要添加到的工作隊列的屬性就可以确定将由哪一個

    worker pool來執行,并且會将該work item添加到該

    worker pool共享的工作清單中。例如,當一個work 

    item被添加到一個workqueue時,它要麼被添加到普通

    的worker-pool的工作清單,要麼被添加到高優先級的

    worker-pool的工作清單,這裡的兩個worker-pool對應

    于添加work item到工作隊列的CPU。

    管理一個工作者線程池的并發度一直是工作者線程池面

    臨的一個重要的問題。cmwq保持了最小的并發度,但是

    又讓CPU不會空閑,充分利用了CPU資源。

    每一個綁定到CPU的線程池通過在排程器中添加鈎子實

    現了并發的控制。當一個工作項被喚醒或者睡眠的時候,

    工作者線程池都會接到通知,以此來追蹤目前并發的工

    作者線程數。一般來說,當一個CPU上有一個或者多個

    工作者線程在執行的時候,與該CPU綁定的工作者線程

    池不會再起動其他的工作者線程,當該CPU上最後一個

    工作者線程睡眠後,立刻啟動一個新的工作,來保證CPU

    不會空轉。

    對于未綁定到CPU的工作隊列來說,它的線程池數量是

    動态的。可以通過apply_workqueue_attrs函數來設定這

    個未綁定的工作隊列的參數,系統會自動生成與這些參

    數對應的工作者線程池。另外對于綁定的工作隊列可以

    設定某些參數,讓某個工作隊列忽略并發的限制。

    任何需要多個工作者線程同時執行的子系統或者驅動程

    序都需要使用有急救工作者線程(rescuer worker)的

    工作隊列。例如,在記憶體回收的時候,回收記憶體的工作

    項往往需要同時執行,是以如果沒有使用這種工作隊列

    的話,就會造成死鎖,因為後執行的工作者線程會等待

    前面的工作者線程被釋放。

4.  應用程式接口(API)

    alloc_workqueue負責建立一個工作隊列,之前的create_*workqueue

    之類的接口已經被丢棄了。該函數有三個參數@name,

    @flags以及@max_active,@name表示該工作隊列的名字,

    如果急救工作者線程也存在的話,那麼@name也是該急救

    工作者線程的名字。

    工作隊列已經不再管理執行資源,但是它作為一個域用

    來管理工作項,例如保證工作項往前執行、flush以及

    工作項的屬性。@flags和@max_active參數控制工作項

    的執行資源配置設定,如何被排程以及如何執行。

    @flags:

    WQ_UNBOUND

    未綁定的工作隊列對應的工作者線程池數是動态的。該

    工作者線程池未綁定到任何的CPU,是以相當于是一個簡

    單的執行上下文的提供者,它會立即執行添加到該工作

    隊列工作項。一般在下面兩種情況會用到:

    * 工作者線程的并發級别波動很大,且将工作項添加到

      工作隊列的發起者會在不同的CPU之間來回執行,因

      此如果不使用未綁定工作隊列的話,就會造成在各個

      CPU的工作者線程池上建立了許多不會使用的工作者

      線程。

    * 當CPU的負擔很重時,最好将工作項添加到位綁定的工

      作隊列,由排程器來進行排程。

    WQ_FREEZABLE

    當系統暫停時,工作隊列會進入到當機狀态。并且會清

    空該工作隊列中的工作項,直到被解凍才可以執行新的

    工作。

    WQ_MEM_RECLAIM

    任何可能在記憶體回收路徑上使用的工作隊列都必須設定

    該标記,它保證了不管記憶體壓力有多大,都至少有一個

    執行上下文與之對應。

    WQ_HIGHPRI

    高優先級工作隊列的工作項被加入到相應CPU的高優先

    級工作者線程池。高優先級工作者線程池由提高了nice

    級别的線程來服務。

    普通的工作者線程池和高優先級的工作者線程池是隔離

    的,他們之間不進行任何的互動。對于并發級别的控制

    也是各自獨立進行控制的。

    WQ_CPU_INTENSIVE

    這種工作隊列中的工作項并不受并發級别的控制,因為

    該類型的工作項通常會需要很多的CPU使用量,是以最

    好的辦法就是由系統排程器來進行排程。

    另外,并發級别的限制會影響密集型工作項的執行,例

    如目前正在運作的非密集型的工作會延遲密集型工作的

    執行。

    該标記對未綁定的工作隊列是無效的。

    @max_active:

    表示每個工作隊列同時最多能有幾個工作項在同一個CPU

    上運作。例如,max_active=16,表示同時最多能有16個

    該工作隊列的工作項在每一個CPU上運作。

    對于綁定的工作隊列,@max_active的最大值為512,默

    認情況下,該參數傳值為0,此時它的最大值為256。對

    于未綁定的工作隊列,max_active的最大值要大于512。

    工作隊列的同時處于活躍狀态的工作項的數目是由使用者

    調節的,例如使用者同時添加到工作隊列中的工作項的數

    目。除非有需求調節活躍工作項的數目,否則,推薦該

    參數指派為0。

    有些需要依賴ST工作隊列的順序,是以可以使WQ_UNBOUND

    和@max_active為1。這樣就模拟了ST,每一個該類型的

    工作項都應該被添加到未綁定的工作隊列中,且一次隻

    能有一個工作項執行就限定了執行的順序。

5.  指導方針

    * 如果工作隊列中的工作項可能用在記憶體回收代碼路徑

      中,一定要設定WQ_MEM_RECLAIM,每一個該類型的隊

      列都有一個保留的執行上下文。另外,如果在記憶體回

      收路徑中有多個工作項互相依賴的話,應該将這些工

      作項添加到不同的工作對列中。

    * 如果沒有特殊要求的話,推薦@max_active參數的值

      為0。

    * 一般如果沒有執行次序要求的話,不會用到ST。

    * 工作隊列作為工作項一個域,用來WQ_MEM_RECLAIM,

      flush以及某些工作項的共有屬性。如果工作項不會

      用到上面的任何一個特性,可以使用系統工作隊列。

    * 除非一個工作項要耗費很多的CPU,一般将工作項添

      加到綁定的工作隊列中。

ref

===

1. Documentation/workqueue.txt

繼續閱讀