linux下的工作佇列

pythontab發表於2013-02-20

對於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上執行,可重入,必須明確的使用自旋鎖.


相關文章