Linux內核工做隊列

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(&gtp_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, &gtp_esd_check_work, ts->clk_tick_cnt);
    ...
    ## 5.等待延時任務完成
    cancel_delayed_work_sync(&gtp_esd_check_work);
    ...
}

static int goodix_ts_remove(struct i2c_client *client)
{
 ...
 ## 6.銷燬工做隊列gtp_esd_check_workqueue
    destroy_workqueue(gtp_esd_check_workqueue);
 ...
}

注:工做隊列容許任務從新調度和睡眠。