Chinaunix首页 | 论坛 | 博客
  • 博客访问: 162820
  • 博文数量: 84
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-09 10:55
文章分类
文章存档

2014年(84)

我的朋友

分类: LINUX

2014-05-15 15:59:36

原文地址:linux下的工作队列 作者:linuxDOS

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





阅读(688) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~