linux 内核 工作队列,Linux内核新旧工作队列机制的剖析和比较
摘要:在中斷驅動的程序設計中,工作隊列是一種強有力的工具。但是在Linux2.6.35及其以前的內核版本中,每創建一個工作隊列就創建與CPU數目相同的內核線程,耗費大量的內核資源;工作只能嚴格串行的處理,效率低。為了適應大規模多處理器硬件平臺,提高處理效率,Linux2.6.36內核開發了受控并發工作隊列機制。這種新機制由內核根據需要創建或銷毀線程,工作可以并發的處理,可望替代之前長期使用的專用線程工具。文章詳細介紹和剖析新工作隊列機制,并通過實驗,對比新舊工作隊列機制的資源消耗和工作效率。結果表明,新工作隊列機制大大減少內核資源的耗費,提高了處理效率。
關鍵詞: Linux內核; 中斷驅動; 工作隊列; 受控并發工作隊列
中圖分類號:TP316.81 文獻標識碼:A 文章編號:1009-3044(2014)06-1227-04
Analysis and Comparison of the New and Old Work Queue in Linux Kernel
MA Jun-qiang
(School of Information Science and Engineering, Xiamen University, Xiamen 361005, China)
Abstract: Work Queue is a powerful tool in interrupt-driven programming. But in Linux kernels of 2.6.35 and before, it consumes a lot of kernel resources in that whenever a Work Queue is created, the same number of kernel threads as CPUs are created. The efficiency to processing works in Work Queue is low due to strictly sequential processing. In order to adapt the hardware platforms with large-scale multiprocessors and increase the processing efficiency, the Linux kernel of 2.6.36 developed Concurrency Managed Workqueue. With this new mechanism, the kernel creates and destroys the threads according to the processing requirements, and works can be concurrently processed. The new Work Queue is hoped to replace the private thread tools that had been used long time. The paper introduces and analyses this new mechanism in detail, and compares the resource consumes and processing efficiency between the new and old mechanisms by experiments. The results show that the new mechanism of Work Queue greatly reduces consumes of kernel resources and increases the processing efficiency.
Key words: Linux kernel; interrupt-driven; work queue; concurrency managed workqueue
當一個中斷發生時,并不是相關的各個操作都具有相同的急迫性,把所有的操作都放進中斷處理程序本身也不合適。Linux內核將整個中斷處理流程簡單分為兩個部分,第一部分是中斷處理程序,稱為上半部(Top Half),在運行時禁止可屏蔽中斷,用于完成關鍵性的、緊急的處理;其余部分稱為下半部(Bottom Half),在運行時開中斷,主要完成與中斷處理密切相關的工作,即延后執行工作(Deferring Work)。Linux內核提供三種不同形式的下半部實現機制:軟中斷(Softirq)、Tasklet和工作隊列(Work Queue)。其中軟中斷和Tasklet運行在中斷上下文中,不可阻塞。而工作隊列由特殊的內核線程—工作者線程(Worker Thread)來執行,運行在進程上下文,可以阻塞。但是由于舊工作隊列機制的缺陷,其應用并不普遍,取而代之的是使用專用線程池,如flush-x:y,bdi-default等。新工作隊列機制為內核提供一種通用的線程池機制,以替代這些專用線程池[1]。實際上,只要涉及中斷驅動的程序設計,工作隊列都是一種強有力的工具,比如在實時控制系統中,可以創建工作隊列方便高效地響應和執行由外部事件驅動的任務。文章將重點介紹和分析新工作隊列機制。對新舊工作隊列機制的介紹分析,選擇的內核版本分別是2.6.36和2.6.35。
工作隊列的設計思想可以類比于現實中的生產流水線 [2]:流水線相當于工作隊列中的worklist鏈表,加工部件相當于中斷發生時所產生的工作序列,工人就是工作者線程。當中斷發生時,內核將本次中斷延后執行的工作序列,放到worklist工作鏈表中,喚醒工作者線程執行工作。工作者線程在執行時可能阻塞;當worklist中的工作處理完畢后,工作者線程進入空閑狀態。從Linux 2.5.41內核引入工作隊列直到2.6.35內核, 其運行機制沒有大的改動,主要缺點是,在有N個CPU的計算機中,每當創建一個工作隊列時,就創建N條流水線,為每條流水線創建一個工作者線程,內核可以向這N條流水線提交該種類的工作(由響應中斷的CPU決定)。因此,如果創建X個工作隊列,則需要創建N * X個工作者線程,但是只有當流水線上有工作時,線程才運行,其余流水線上的線程都處于空閑狀態。大量的線程需要消耗線程的ID資源和大量內存,同時也會增加調度器的負擔。這種情況在擁有大量CPU的超級計算機上顯得尤為浪費。另一方面,一條流水線上只有一個工作者線程,因此同一流水線上工作的處理是嚴格串行的,嚴重制約處理的效率。
為適應大規模多處理器硬件平臺,提高工作隊列的處理效率,從2.6.36內核開始對工作隊列進行徹底的改造。在新的工作隊列機制中,內核始終維持N + 1條“工作流水線”,即全局每CPU工作隊列gcwq(Global Percpu Workqueue,詳見1.1節)。新機制的流水線是通用的,所有來自同一個CPU的中斷所產生的工作序列,都放在這條流水線上。每條流水線上的工作者線程“按需分配”,即當一個工作者線程阻塞時,可以讓另一個工作者線程來處理該流水線上剩余的工作。當流水線上需要新的工作者線程時,就創建新線程;而當流水線上線程過多時,就銷毀線程。同一條流水線上可以有多個工作同時被指派給多個工作者線程,當然任何時刻一條流水線上只有一個工作者線程在運行。這種新的工作隊列機制稱為受控并發工作隊列(Concurrency Managed Workqueue)[3-5]。
1 受控并發工作隊列
1.1 全局每CPU工作隊列(gcwq)
如果計算機有N個CPU,則內核創建N + 1個gcwq,其結構如下所示:
struct global_cwq {
spinlock_t lock;
unsigned int cpu;
struct list_head worklist;
int nr_workers;
int nr_idle;
struct list_head idle_list;
struct hlist_head busy_hash[BUSY_WORKER_HASH_SIZE];
struct timer_list idle_timer;
struct timer_list mayday_timer;
...
} ____cacheline_aligned_in_smp;
N個gcwq分別與N個CPU一一綁定,管理相關CPU上的工作者線程和中斷產生的工作;第N + 1個gcwq稱為unbound_global_cwq,其中的工作者線程未與特定的CPU綁定,詳見1.4節WQ_UNBOUND標志說明。雖然gcwq也稱作“工作隊列”,但是與用戶創建的工作隊列不同,它是內部管理結構,對用戶不可見。gcwq中的cpu字段表示與其關聯的CPU編號,worklist雙向鏈表存儲由中斷提交到該CPU上的工作,lock字段為保護gcwq結構體的自旋鎖。每個gcwq都維護管理一個工作者線程池,其中的工作者線程有idle(空閑)和busy(工作)兩種狀態;idle_list雙向鏈表中管理處于idle狀態的工作者線程,nr_idle記錄其數量;為了快速檢索,使用busy_hash哈希鏈表管理處于busy狀態的工作者線程。這些線程負責處理worklist鏈表中工作。nr_workers記錄工作者線程池中線程的數量。
1.2 新工作隊列機制的運行
當中斷發生時,內核調用queue_work函數將工作序列提交到gcwq。若相關的線程池中沒有線程,則內核創建工作者線程;否則喚醒一個工作者線程(異常情況處理見1.4節WQ_RESCUER屬性)。工作者線程調用worker_thread函數,該函數在執行中使用gcwq中的自旋鎖進行保護,并完成以下動作:
1)線程從idle狀態變為busy狀態。
2)對所屬的gcwq的線程池進行檢查管理。設線程A是當前正在執行的線程,若worklist上有多個待處理的工作,則A檢查線程池中是否還有處于idle狀態的線程,如果存在,設選中的為線程B;否則A將創建并喚醒一個新線程B,B在創建后進入idle狀態。因此在處理工作時,線程池中將保持至少一個處于idle狀態的線程待命,以便迅速響應和處理worklist上的后繼工作。
3)線程A從gcwq的worklist鏈表中依次取出未處理的工作進行處理。當處理完worklist中所有的工作后,將再一次對線程池進行檢查管理,然后進入idle狀態并休眠。
4)一旦線程A阻塞且worklist上還有待處理的工作,則線程B開始運行,它調用worker_thread函數重復以上過程。
如3)所述,當工作者線程處理完全部工作后將對線程池進行一次檢查管理。此時如果gcwq中空閑的工作者線程過多,其判斷條件是nr_idle > 2且(nr_idle - 2) * 4 >= nr_idle,則gcwq將銷毀idle狀態持續時間超過5分鐘的工作者線程。每個gcwq的線程池最終將維護兩個處于idle狀態的工作者線程。
1.3新工作隊列機制的改進
1)由內核根據處理需求,控制工作者線程的創建和銷毀,避免創建過多的內核線程。在工作隊列空閑時,新機制中的線程數大致為( N + 1 ) * 2,工作隊列數量與內核線程數基本無關(除非工作隊列設置WQ_RESCUER標志,見1.4節)。這個改進大大減少內核資源的消耗。
2)同一CPU上一個工作隊列中的工作可以并發的處理。這種并發處理方式相比舊機制的嚴格串行,提高了處理效率。在每個CPU上,一個工作隊列中可并發處理的工作數目是有限制的(見1.4節max_active參數),當達到限制時,將不再喚醒新的工作者線程。
3)創建工作隊列時可以指定工作隊列的屬性。用戶可以根據工作性質的不同創建不同的工作隊列,如高優先級的(WQ_HIGHPRI)、未綁定的(WQ_UNBOUND)、不可重入的(WQ_NON_REENTRANT)、帶救援者線程的(WQ_RESCUER)[3][5]等。在圖1中,gcwq的worklist鏈表中的工作分為高優先級的工作和普通優先級的工作兩類,高優先級的工作排在鏈表頭部,普通優先級的工作排在鏈表尾部,同一類別的工作之間按照提交的順序排列。而在舊工作隊列機制中則沒有這些屬性,工作按照提交的順序被執行。
圖1 gcwq的worklist鏈表中的工作分布
4)新工作隊列機制提供4個預定義工作隊列,方便用戶使用工作隊列。這四個預定義的工作隊列分別為events、events_long、events_nrt、events_unbound。其中events是普通工作隊列,要求其中的工作執行時間盡量短;需要長時間處理的工作可以提交到events_long隊列中;events_nrt是不可重入工作隊列,其中的工作將不會在多個CPU上并發執行;events_unbound隊列就是設置了WQ_UNBOUND標志的隊列,只要處理的工作數量未達到限制,其中的工作就可以立即被處理。用戶可以根據需要使用這4個預定義工作隊列,當然也可以自己創建工作隊列。
1.4 創建工作隊列
調用下面的宏創建一個工作隊列:alloc_workqueue(name, flags, max_active)。
參數name:指定工作隊列的名稱。
參數flags:標志位,指明工作隊列的屬性,摘要解釋如下:
WQ_NON_REENTRANT:默認時,工作隊列中的多個同一種類的工作,在一個CPU上不可重入,但是允許在不同CPU上重入(即允許在不同CPU上并發的處理)。如果設置此標志,則工作隊列中的多個同一種類的工作在不同CPU上也不可重入。此時工作可能需要從響應中斷的CPU遷移到另一個CPU上:當中斷產生工作時,如果以前產生的同類工作正在另一個CPU上處理,則將該工作提交到這個CPU上。
WQ_UNBOUND:如果設置此標志,則內核將為工作隊列設置WQ_HIGHPRI標志,其上的工作都將插入到unbound_global_cwq的worklist鏈表上。unbound_global_cwq中工作將按照提交的順序被處理。通常,為了更好地利用CPU緩存,工作在所提交的CPU上處理,而unbound_global_cwq中的工作者線程未與特定的CPU綁定,其中的工作可能運行在任意一個CPU上,因此無法有效利用CPU緩存。此標志是為需要大量CPU周期的工作設置的,此時各個CPU的負載均衡更為重要,所以此類工作最好由調度器決定在哪一個CPU上運行。
WQ_RESCUER:如果設置此標志,則為工作隊列專門創建一個救援者線程(Rescure Thread)。創建救援者線程的目的是避免長時間的等待或死鎖。由于內核創建工作者線程時使用GFP_KERNEL標志來分配內存,可能導致創建過程長時間阻塞,因此在創建時,內核設置一個定時器mayday_timer。如果定時器超時,但線程仍未創建成功,那么內核就喚醒各個工作隊列中的救援者線程,執行rescuer_thread函數處理的剩余工作。所有在處理時可能與內存回收執行路徑重疊的工作隊列,必須設置這個標志。
WQ_HIGHPRI:如果設置此標志,則該工作隊列中的工作都是高優先級的,其中的工作將被插入到目標gcwq的工作列表worklist的最后一個高優先級工作后面,即高優先級的工作排在worklist隊列頭,且依據提交的順序被處理。只要資源可用,總是盡可能快地處理高優先級工作。
參數max_active:一個工作隊列在每個CPU上可能并發執行的最大工作數目。對綁定的工作隊列,max_active最大值512;默認值為0,此時max_active為256;對于未綁定的工作隊列,max_active的最大值為max(512, 4 * num_possible_cpus())。建議內核開發者使用默認值。
2 實驗設計和結果
通過兩個實驗來對比新舊工作隊列機制的資源消耗和工作效率,實驗環境如表1所示。
表1 實驗環境
2.1新舊工作隊列機制中可創建的最大工作隊列數
表2 最大工作隊列數
在本實驗中,測試機器的pid_max設置為32768。由表2可以看出,在舊工作隊列機制中,可創建的工作隊列數目隨著CPU數目的增加而減少,主要受限于內核可創建的線程數;在新工作隊列機制中,可創建的工作隊列數目隨著內存的增加而增加,與系統可創建的最大線程數無關。新工作隊列機制中的最大工作隊列數主要受限于內核可分配的percpu空間。這是因為每創建一個工作隊列都需要分配一定大小且地址對齊的percpu空間,隨著創建的工作隊列數的增加,內核中將沒有可用的percpu空間,從而導致創建工作隊列失敗。
2.2在新舊工作隊列機制中,同一CPU上一個工作隊列中10個工作的執行效率的比較
實驗所用的工作隊列不帶任何標志位,每個工作分別有三種休眠時間0s、1s或5s,用來模擬處理的時間。新工作隊列機制的并發工作數有1、5、256三種情況。實驗結果見表3。
表3 同一CPU上一個工作隊列中10個工作的處理時間
如果所有工作都不休眠或者max_active等于1,則新舊工作隊列機制下10個工作的處理時間基本相同。當工作有休眠且max_active大于1時,一旦處理工作的線程休眠,則內核立即喚醒新的線程執行后繼的工作,直到正在執行的工作數等于max_active。實驗結果表明,新工作隊列機制可以顯著提高處理的效率。
3 結束語
舊工作隊列機制應用于大規模多處理器硬件平臺會耗費大量的內核資源,工作的處理效率也很低。以往的補救方法是使用專用線程工具。Linux2.6.36內核開發受控并發工作隊列,由內核“按需分配”工作者線程,大大減少內核資源的耗費;同時,工作可以并發處理,提高了處理效率。新機制提供的通用的線程池有望替代內核中專用線程池,成為中斷驅動程序設計的強有力的工具。
新工作隊列機制的設計也存在一些缺陷。首先,優先級只分為高優先級和普通優先級,略顯粗糙,無法區分一個工作隊列中不同工作之間的差別。其次,工作隊列的優先級與內核線程的優先級無關,在資源比較緊張時,可能無法滿足硬實時任務的需要。再有,max_active參數的設置不是針對一個工作隊列,而是針對一個CPU上所有工作隊列,也不夠靈活。如果這些問題得到改進,新機制將能更方便地應用于實時系統。
參考文獻:
[1] Tejun Heo .backing-dev: replace private thread pool with workqueue.txt [EB/OL]. [2010-09].http:///Articles/403653.
[2] 陳學松.深入Linux設備驅動程序內核機制[M].北京:電子工業出版社,2012:214-230.
[3] Tejun Heo.workqueue.txt [EB/OL]. [2010-09].http://lxr.linux.no/linux+v-2.6.36.4/Documentation/workqueue.txt.
[4] Jonathan Corbet .Concurrency-managed workqueues [EB/OL]. [2010-09].http:///Articles/355700.
[5] Jonathan Corbet .Working on workqueues [EB/OL]. [2010-09].http:///Articles/403891.
總結
以上是生活随笔為你收集整理的linux 内核 工作队列,Linux内核新旧工作队列机制的剖析和比较的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 万代南梦宫街机游戏新作《偶像大师 TOU
- 下一篇: 黑龙江省职工医保缴费标准是多少?