本文檔的Copyleft歸rosetta所有,使用GPL發布,可以自由拷貝、轉載,轉載時請保持文檔的完整性。
參考資料:《Linux內核設計與實現》第3版 LKD3e、linux-2.6.27
工作隊列子系統是一個用於調用創建內核線程的接口,通過它創建的線程負責執行由內核其它部分排到隊列裡的任務。這些內核線程稱為工作者線程。工作隊列子系統提供了一個缺省的工作都線程來處理工作。一般使用缺省線程即可,但當處理密集型和性能要求嚴格的任務時,創建擁有自己的工作者線程比較好。(引至LKD3e)
這個接口就是create_workqueue(),它返回一個struct workqueue_struct 結構指針。
/*
* The externally visible workqueue abstraction is an array of
* per-CPU workqueues:
*/
struct workqueue_struct {
struct cpu_workqueue_struct *cpu_wq;
struct list_head list;
const char *name;
int singlethread;
int freezeable; /* Freeze threads during suspend */
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
上面注釋:外部可見的工作隊列抽象是由per-CPU的工作隊列組成的數組,這個數組為結構體 cpu_workqueue_struct 。所以每種任務,它有一個自己的工作隊列(struct workqueue_struct ),這個工作隊列如果需要的話會為每個CPU創建對應的工作者線程。也就是說每個CPU,每個工作者線程對應一個cpu_workqueue_struct。
/*
* The per-CPU workqueue (if single thread, we always use the first
* possible cpu).
*/
struct cpu_workqueue_struct {
spinlock_t lock;
struct list_head worklist;
wait_queue_head_t more_work;
struct work_struct *current_work;
struct workqueue_struct *wq;
struct task_struct *thread;
int run_depth; /* Detect run_workqueue() recursion depth */
} ____cacheline_aligned;
剛才說了,每種任務,它都有一個自己的工作隊列,這種任務的抽象就是為每個CPU創建一個處理這種任務的工作者線程(當然這是需要的情況下,如果不需要則會使用默認的工作者線程events/n,n為CPU編號),那麼這個wq就是關聯到自己的工作隊列workqueue_struct。所有的工作者線程都是用普通的內核線程實現的,由worker_thread()函數完成。
當為一個CPU創建完一個線程後,這個線程執行死循環開始休眠,當有操作插入到隊列時,線程被喚醒並執行。(LKD3e)
對應的具體工作由work_struct結構:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
};
由list_head可知,它是個雙向鏈表,每個結點為一個work_struct結構類型。每個CPU上的每種類型的隊列都對應這樣一個鏈表。當一個工作線程被喚醒時,它會執行這個鏈表上的所有工作;工作執行完畢後就從鏈表上先移除相應的work_struct;當鏈表上不再有對象時就繼續休眠。
總得來說就是每種任務(可以理解成為處理不同數據結構),有一個workqueue_struct。每個CPU有多個工作者線程,
每個線程處理相應的任務。處理過程最終調用的是func。至於func是怎麼賦值的可參考下面代碼實現。