什麼是 workqueue ?
Linux 中的 Workqueue 機制就是為了簡化核心執行緒的建立。 通過呼叫 workqueue 的介面就能建立核心執行緒。 並且可以根據當前系統 CPU 的個數建立執行緒的數量,使得執行緒處理的事務能夠並行化。
Linux 中的 Workqueue 機制就是為了簡化核心執行緒的建立。 通過呼叫 workqueue 的介面就能建立核心執行緒。 並且可以根據當前系統 CPU 的個數建立執行緒的數量,使得執行緒處理的事務能夠並行化。
workqueue 是核心中實現簡單而有效的機制,他顯然簡化了核心 daemon 的建立,方便了用戶的編程,
Workqueue 機制的實現
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;/* 任務延時處理定時器 */
};
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 都建立一個核心執行緒。
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 。
@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後,停止運行工作
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);
//用完後刪掉工作隊列
//用完後刪掉工作隊列