2015年2月28日 星期六

[轉] Linux中Workqueue機制分析

什麼是 workqueue ?
Linux 中的 Workqueue 機制就是為了簡化核心執行緒的建立。 通過呼叫 workqueue 的介面就能建立核心執行緒。 並且可以根據當前系統 CPU 的個數建立執行緒的數量,使得執行緒處理的事務能夠並行化。

workqueue 是核心中實現簡單而有效的機制,他顯然簡化了核心 daemon 的建立,方便了用戶的編程,



Workqueue 機制的實現
Workqueue 機制中定義了兩個重要的數據結構,分析如下:

1、  cpu_workqueue_struct 結構。 該結構將CPU 和核心執行緒進行了綁定。 在建立 workqueue 的過程中, Linux 根據當前系統 CPU 的個數建立 cpu_workqueue_struct 。 在該結構主要維護了一個任務隊列,以及核心執行緒需要睡眠的等待隊列,另外還維護了一個任務上下文,即 task_struct 。

2、  work_struct 結構是對任務的抽象。 在該結構中需要維護具體的任務方法,需要處理的數據,以及任務處理的時間。 該結構定義如下:

struct work_struct {
 unsigned long pending;
 struct list_head entry;/* 將任務掛載到 queue 的掛載點 */
 void (*func)(void *);  /* 任務方法 */
 void *data;            /* 任務處理的數據 */
 void *wq_data;         /* work 的屬主 */
 strut timer_list timer;/* 任務延時處理定時器 */
};

      

       當用戶呼叫 workqueue 的初始化介面 create_workqueue 或者 create_singlethread_workqueue 對 workqueue 隊列進行初始化時,核心就開始為用戶分配一個 workqueue 對象,並且將其鏈到一個全局的 workqueue 隊列中。 然後 Linux 根據當前 CPU 的情況,為 workqueue 對象分配與 CPU 個數相同的 cpu_workqueue_struct 對象,每個 cpu_workqueue_struct 對像都會存在一條任務隊列。 緊接著, Linux 為每個 cpu_workqueue_struct 對象分配一個核心 thread ,即核心 daemon 去處理每個隊列中的任務。 至此,用戶呼叫初始化介面將 workqueue 初始化完畢,返回 workqueue 的指標。



       在初始化 workqueue 過程中,核心需要初始化核心執行緒,註冊的核心執行緒工作比較簡單,就是不斷的掃描對應 cpu_workqueue_struct 中的任務隊列,從中獲取一個有效任務,然後執行該任務。 所以如果任務隊列為空,那麼核心 daemon 就在 cpu_workqueue_struct 中的等待隊列上睡眠,直到有人喚醒 daemon 去處理任務隊列。



       Workqueue 初始化完畢之後,將任務運行的上下文環境構建起來了,但是具體還沒有可執行的任務,所以,需要定義具體的 work_struct 對象。 然後將 work_struct 加入到任務隊列中, Linux 會喚醒 daemon 去處理任務。



       上述描述的 workqueue 核心實現原理可以描述如下:

点击看大图
    在 Workqueue 機制中,提供了一個系統默認的 workqueue 隊列—— keventd_wq ,這個隊列是 Linux 系統在初始化的時候就建立的。 用戶可以直接初始化一個 work_struct 對象,然後在該隊列中進行調度,使用更加方便。



Workqueue 編程介面

1
 create_workqueue
 用於建立一個 workqueue 隊列,為系統中的每個 CPU 都建立一個核心執行緒。

 輸入參數:
@name : workqueue 的名稱

2
 create_singlethread_workqueue 用於建立 workqueue ,只建立一個核心執行緒。

輸入參數:
@name : workqueue 名稱

3
 destroy_workqueue
 釋放 workqueue 隊列。

輸入參數:
@ workqueue_struct :需要釋放的 workqueue 隊列指標

4
 schedule_work
 調度執行一個具體的任務,執行的任務將會被掛入 Linux 系統提供的 workqueue —— keventd_wq

輸入參數:
@ work_struct :具體任務對象指標

5
 schedule_delayed_work
 延遲一定時間去執行一個具體的任務,功能與 schedule_work 類似,多了一個延遲時間,

輸入參數:
@work_struct :具體任務對象指標
@delay :延遲時間

6
 queue_work
 調度執行一個指定 workqueue 中的任務。

輸入參數:
@ workqueue_struct :指定的 workqueue 指標
@work_struct :具體任務對象指標

7
 queue_delayed_work
 延遲調度執行一個指定 workqueue 中的任務,功能與 queue_work 類似,輸入參數多了一個 delay 。

基本順序

1.創建工作隊列
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
2.創建工作
DECLARE_WORK(name, void (*function)(void *), void *data);
//一個函數搞定定義和初始化
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);
//要先定義work_struct,再調用這兩個函數
//INIT_WORK做更全的初始化,若需要改變工作隊列,則用PREPARE_WORK


3.提交工作給一個工作隊列
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);
//返回0,添加成功,非0表明已存在此工作
int cancel_delayed_work(struct work_struct *work);
//返回非0,取消成功,返回0,取消不成功,工作仍在運行
void flush_workqueue(struct workqueue_struct *queue);
//確保cancel_delayed_work調用返回0後,停止運行工作

4.刪除隊列
void destroy_workqueue(struct workqueue_struct *queue);
//用完後刪掉工作隊列

沒有留言:

張貼留言