linux下的工作队列

内容摘要
对于linux中的工作队列,当然有很多书上都写了,网上也有很多文章反复的写.但是这里还是要写一写,作为内核中虽然小但是很重要的一个应用,基础要打扎实,理清原理.在3.1.1内核中和
文章正文

对于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上运行,可重入,必须明确的使用自旋锁.


代码注释
[!--zhushi--]

作者:喵哥笔记

IDC笔记

学的不仅是技术,更是梦想!