原帖見:www.embexperts.com
先簡單快速總結一下,更詳細的剖析後續用文章編輯方式逐步完成。
分成兩大部分,第一部分是用來執行work queue中每個節點上掛載的函數的核心線程,第二部分是從驅動程式的角度看work queue的使用。
第一部分 worker_thread核心線程
Linux系統啟動期間會建立一名為worker_thread線程,該線程建立之後就處於sleep狀態。這裡所謂的核心線程,從調度器的角度就是一可以調度的進程,從代碼的表現形式看,就是一函數。系統建立的這個worker_thread線程基於一workqueue_struct結構變數上(該結構體變數的成員name為"events").
第二部分 work queue的使用
1.只考慮使用系統的keventd管理的工作隊列
驅動程式調用schedule_work向工作隊列遞交新的工作節點,schedule_work內部會喚醒worker_thread核心線程(使之進程狀態為可調度)。在下一次進程調度時刻,worker_thread被調度執行,其主要任務便是調用它所管理工作隊列中每個工作節點上掛載的函數,調用完畢該工作節點會從任務隊列中被刪除。當所有節點上的函數調用完畢,worker_thread繼續sleep,直到schedule_work再次被某個驅動程式調用。
與使用驅動程式自己建立的工作對列的區別是:schedule_work內部是調用queue_work(keventd_wq, work),而使用驅動程式自己建立的工作隊列在調用queue_work時的第一個參數是驅動程式自己建立的工作隊列。
2.驅動程式使用自己建立的工作隊列
這種情況驅動程式調用create_workqueue。該函數的原理跟1中基本是一樣的,只不過再會建立一個核心進程,該核心進程的核心功能名字依然為worker_thread,只不過這個worker_thread工作在新的屬於驅動程式自己的工作隊列。
使用方法是:
a. 調用create_workqueue產生屬於驅動程式自己的work queue. struct workqueue_struct *my_workqueue = create_workqueue("my_workqueue");
b.調用queue_work象a中產生的my_workqueue中註冊工作節點, queue_work(my_workqueue, work)
這兩種情況下的核心線程其實都是利用了一個名為kthreadd的核心線程來建立工作隊列的worker_thread,其本質是向全域列表kthread_create_list中加入相應的核心線程函數節點,由kthreadd來負責建立進程架構,然後運行kthread_create_list中加入的節點上的核心線程函數。
向kthread_create_list中每加入一個核心線程函數節點,都會喚醒kthreadd線程。
當等待隊列是空時,worker_thread會睡眠在該等待隊列中。當driver調用queue_work時,再向等待隊列中加入工作節點的同時,會喚醒睡眠在該等待隊列上的worker_thread線程。
|
create_singlethread_workqueue(name)與create_workqueue(name)
Driver調用這兩個宏來建立自己的工作隊列以及相應的核心進程(其核心線程函數為worker_thread,下來為了方便敘述,就簡稱該進程為worker_thread進程)
1. create_singlethread_workqueue(name)
該函數的實現機制如所示,函數返回一個類型為struct workqueue_struct的指標變數,該指標變數所指向的記憶體位址在函數內部調用kzalloc動態產生。所以driver在不再使用該work queue的情況下調用void destroy_workqueue(struct workqueue_struct *wq)來釋放此處的記憶體位址。
圖中的cwq是一per-CPU類型的地址空間。對於create_singlethread_workqueue而言,即使是對於多CPU系統,核心也只負責建立一個worker_thread核心進程。該核心進程被建立之後,會先定義一個圖中的wait節點,然後在一迴圈體中檢查cwq中的worklist,如果該隊列為空白,那麼就會把wait節點加入到cwq中的more_work中,然後休眠在該等待隊列中。
Driver調用queue_work(struct workqueue_struct *wq, struct work_struct *work)向wq中加入工作節點。work會依次加在cwq->worklist所指向的鏈表中。queue_work向cwq->worklist中加入一個work節點,同時會調用wake_up來喚醒休眠在cwq->more_work上的worker_thread進程。wake_up會先調用wait節點上的autoremove_wake_function函數,然後將wait節點從cwq->more_work中移走。
worker_thread再次被調度,開始處理cwq->worklist中的所有work節點...當所有work節點處理完畢,worker_thread重新將wait節點加入到cwq->more_work,然後再次休眠在該等待隊列中直到Driver調用queue_work...
相對於create_singlethread_workqueue, create_workqueue同樣會分配一個wq的工作隊列,但是不同之處在於,對於多CPU系統而言,對每一個CPU,都會為之建立一個per-CPU的cwq結構,對應每一個cwq,都會產生一個新的worker_thread進程。但是當用queue_work向cwq上提交work節點時,是哪個CPU調用該函數,那麼便向該CPU對應的cwq上的worklist上增加work節點。 |