Linux內核中實現工做推後執行的方法有:軟中斷、tasklet
和工做隊列(work queue
)。linux
本文介紹工做隊列的簡單用法。函數
1、工做任務定義
Linux內核中的工做隊列包括:共享工做隊列和自定義工做隊列。區別以下:spa
1)共享工做隊列:將新建立的工做任務添加到Linux內核建立的全局工做隊列system_wq中;線程
2)自定義工做隊列:將新建立的工做任務添加到本身建立的工做隊列中;指針
一、共享工做隊列rest
1)、靜態定義code
宏:DECLARE_WORK(n, f)
,文件:include/linux/workqueue.h
,定義以下:接口
#define DECLARE_WORK(n, f) \ struct work_struct n = __WORK_INITIALIZER(n, f)
參數:隊列
n:表示工做任務的名稱;ci
f:表示工做任務的實現函數;
相似接口:DECLARE_DELAYED_WORK(n, f)
,建立延時工做任務。
2)、動態定義
文件:include/linux/workqueue.h
,定義以下:
#define INIT_WORK(_work, _func) \ __INIT_WORK((_work), (_func), 0)
參數:
_work
:表示工做任務的名稱;
_func
:表示工做任務的實現函數;
二、自定義工做隊列
文件:include/linux/workqueue.h
,定義以下:
#define create_workqueue(name) \ alloc_workqueue("%s", WQ_MEM_RECLAIM, 1, (name)) #define create_singlethread_workqueue(name) \ alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name)
參數:
name:工做隊列名稱。傳入值爲字符串,和共享工做隊列裏的參數不一樣。
返回值:工做隊列指針
2、經常使用接口說明
工做任務定義 | 工做任務添加 | 工做任務清除 | 工做任務取消 |
---|---|---|---|
DECLARE_WORK() | schedule_work() | flush_work() | cancel_work_sync() |
DECLARE_DELAYED_WORK() | schedule_delayed_work() | flush_delayed_work() | cancel_delayed_work() cancel_delayed_work_sync() |
INIT_WORK() | schedule_work() | flush_work() | cancel_work_sync() |
INIT_DELAYED_WORK() | schedule_delayed_work() | flush_delayed_work() | cancel_delayed_work() cancel_delayed_work_sync() |
create_workqueue() | queue_work() queue_delayed_work() queue_work_on() | flush_workqueue() | destroy_workqueue() |
create_singlethread_workqueue() | queue_work() | flush_workqueue() | destroy_workqueue() |
注:
一、flush_work()
:堵塞工做任務,直到工做任務完成
二、flush_delayed_work()
:等待延時工做任務完成
三、cancel_work_sync()
:取消工做任務並等待它完成
四、cancel_delayed_work()
:取消延時工做任務
五、cancel_delayed_work_sync()
:取消延時工做任務並等待它完成
六、create_workqueue()
:對於多CPU系統,內核會在每一個CPU上建立一個工做隊列,使線程處理並行化
七、create_singlethread_workqueue()
:內核只在一個CPU上建立一個工做隊列
八、queue_work_on()
:在指定CPU上添加工做任務,queue_work()
調用queue_work_on()
在全部CPU上添加工做任務
3、接口使用舉例
一、共享工做隊列
文件:drivers/gpu/drm/drm_fb_helper.c
,舉例以下:
## 三、工做任務的具體實現 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { ... } ## 一、定義工做任務,名稱:drm_fb_helper_restore_work,實現函數:drm_fb_helper_restore_work_fn static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); static void drm_fb_helper_sysrq(int dummy1) { ## 二、將drm_fb_helper_restore_work加入到全局工做隊列 schedule_work(&drm_fb_helper_restore_work); }
其它接口使用方法相似。
二、自定義工做隊列
文件:drivers/input/touchscreen/gt9xx/gt9xx.c
## 1.定義工做任務和工做隊列 static struct delayed_work gtp_esd_check_work; static struct workqueue_struct * gtp_esd_check_workqueue = NULL; static int goodix_ts_init(void) { ... ## 2.初始化工做任務gtp_esd_check_work; 建立工做隊列gtp_esd_check_workqueue INIT_DELAYED_WORK(>p_esd_check_work, gtp_esd_check_func); gtp_esd_check_workqueue = create_workqueue("gtp_esd_check"); ... } ## 3.工做任務gtp_esd_check_work的實現函數 static void gtp_esd_check_func(struct work_struct *work) { ... } void gtp_esd_switch(struct i2c_client *client, s32 on) { ... ## 4.將工做任務gtp_esd_check_work添加到工做隊列gtp_esd_check_workqueue,延時調度 queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, ts->clk_tick_cnt); ... ## 5.等待延時任務完成 cancel_delayed_work_sync(>p_esd_check_work); ... } static int goodix_ts_remove(struct i2c_client *client) { ... ## 6.銷燬工做隊列gtp_esd_check_workqueue destroy_workqueue(gtp_esd_check_workqueue); ... }
注:工做隊列容許任務從新調度和睡眠。