對於linux中的工作佇列,當然有很多書上都寫了,網上也有很多文章反覆的寫.但是這裡還是要寫一寫,作為核心中雖然小但是很重要的一個應用,基礎要打紮實,理清原理.在3.1.1核心中和以前核心中有了些許變化. 這裡參考資料:《深入linux裝置驅動程式核心機制》、《深入理解linux核心》 《精通linux驅動程式開發》等.
核心中基本結構定義和基本操作函式:kernel/workqueue.c
include/linux/workqueue.c
這裡並不準備先介紹結構體定義和操作函式或者宏,而是先看一個實際程式碼的例子:
drivers/isdn/capi/kcapi.c
/*
* The notifier will result in adding/deleteing of devices. Devices can
* only removed in user process, not in bh.
*/
static int notify_push(unsigned int event_type, u32 controller)
{
struct capictr_event *event = kmalloc(sizeof(*event), GFP_ATOMIC);
if (!event)
return -ENOMEM;
INIT_WORK(&event->work, do_notify_work);
event->type = event_type;
event->controller = controller;
queue_work(kcapi_wq, &event->work);
return 0;
}
這裡我們只關注粗體部分,INIT_WORK故名思意初始化工作,我們看看它具體做了什麼.
#define INIT_WORK(_work, _func) \
do { \
__INIT_WORK((_work), (_func), 0); \
} while (0)
/*
* initialize all of a work item in one go
*
* NOTE! No point in using "atomic_long_set()": using a direct
* assignment of the work data initializer allows the compiler
* to generate better code.
*/
#ifdef CONFIG_LOCKDEP
#define __INIT_WORK(_work, _func, _onstack) \
do { \
static struct lock_class_key __key; \
\
__init_work((_work), _onstack); \
(_work)->data = (atomic_long_t) WORK_DATA_INIT();\
lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0);\
INIT_LIST_HEAD(&(_work)->entry); \
PREPARE_WORK((_work), (_func)); \
} while (0)
#else
#define __INIT_WORK(_work, _func, _onstack) \
do { \
__init_work((_work), _onstack); \
(_work)->data = (atomic_long_t) WORK_DATA_INIT();\
INIT_LIST_HEAD(&(_work)->entry); \
PREPARE_WORK((_work), (_func)); \
} while (0)
#endif
我們繼續看PREPARE_WORK:
/*
* initialize a work item's function pointer
*/
#define PREPARE_WORK(_work, _func) \
do { \
(_work)->func = (_func); \
} while (0)
不用多說,很簡單.不過這裡覺得有必要把工作的結構體貼出來:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
還有間接使用的結構體:
struct capictr_event {
struct work_struct work;
unsigned int type;
u32 controller;
};
初始化完工作及其延時要執行的函式,這裡我們看下延時執行的函式(也就是我們想要完成的任務).
static void do_notify_work(struct work_struct *work)
{
struct capictr_event *event =
container_of(work, struct capictr_event, work);
blocking_notifier_call_chain(&ctr_notifier_list, event->type,
(void *)(long)event->controller);
kfree(event);
}
這裡也不多解釋,只需要注意下系統宏 container_of使用的意義就行.
我們回到剛開始的部分,接著是
queue_work(kcapi_wq, &event->work);
也就是把我們的工作加入到工作佇列中,然後由佇列管理延時操作.
/**
* queue_work - queue work on a workqueue
* @wq: workqueue to use
* @work: work to queue
*
* Returns 0 if @work was already on a queue, non-zero otherwise.
*
* We queue the work to the CPU on which it was submitted, but if the CPU dies
* it can be processed by another CPU.
*/
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
int ret;
ret = queue_work_on(get_cpu(), wq, work);
put_cpu();
return ret;
}
下面我們就看看工作佇列的建立:
基本的建立工作佇列函式有兩個:
#define create_workqueue(name) \
alloc_workqueue((name), WQ_MEM_RECLAIM, 1)
******************************
我們看具體的初始化:
static struct workqueue_struct *kcapi_wq;
static int __init kcapi_init(void)
{
int err;
kcapi_wq = alloc_workqueue("kcapi", 0, 0);
if (!kcapi_wq)
return -ENOMEM;
register_capictr_notifier(&capictr_nb);
err = cdebug_init();
if (err) {
unregister_capictr_notifier(&capictr_nb);
destroy_workqueue(kcapi_wq);
return err;
}
kcapi_proc_init();
return 0;
}
到這裡我想大家已經清晰了.
說到工作佇列,我們就不得不提下軟中斷、tasklet. 基本原理是一樣的.有興趣的可以自己看相關程式碼分析學習.這裡就簡單總結下它們的區別和應用場景:
工作佇列:延時操作執行在程式的上下文中,執行睡眠.
tasklet:動態分配,延時操作執行在中斷上下文中,不能睡眠,並且一個tasklet任意時刻,只能執行一個例項.
軟中斷:它是靜態分配的,可以併發的在多個cpu上執行,可重入,必須明確的使用自旋鎖.