[概述]
工作队列是一种将工作推后执行的的形式,工作队列可以把工作推后,交由一个内核线程去执行,占有进程上下文的所有优势,允许重新调度和睡眠。
[工作队列的实现]
工作队列子系统是一个用于创建内核线程的接口,通过它创建的进程负责执行由内核其他部分排到队列里的任务。它创建的这些内核线程称作工作者线程(worker thread)。工作队列子系统提供了一个缺省的工作者线程来处理推后的工作,所以我们大多数情况下,没必要自己去创建工作者线程,使用默认的就OK。
Linux kernel development 3 上对工作队列的实现原理讲的很清楚了,这里就不再详述。具体来看一个实例
[实例]
static struct input_dev *button_dev; static struct work_struct button_wq;
在做实际的驱动开发的时候,不推荐使用全局变量,因为全局变量是造成竞争条件的主要原因。
[中断处理程序]
static irqreturn_t button_interrupt(intirq, void *p) { schedule_work(&button_wq); returnIRQ_RETVAL(IRQ_HANDLED); }
schedule_work(&work)函数用来调度work,把work提交给缺省的events工作者线程(插入工作任务链表中)。work马上会被调度,一旦其所在的处理器上的工作者线程被唤醒,唤醒的工作者队列会遍历整个工作任务链表,如果有工作,它就会被执行,否则继续睡眠。
Schedule_work_delay(&work, delay) 和schedule_work函数的作用相同,唯一区别就是延迟delay时间后才会执行。
[工作队列处理函数]
void work_handler(struct work_struct *data) { /*get pin value <down 0, up 1> */ intval = s3c2410_gpio_getpin(S3C2410_GPG(0)); input_report_key(button_dev,KEY_1, val); input_sync(button_dev); }
完成按键信息的获取和上报工作。
[模块初始化函数]
static int __init button_init(void) { interr; if(request_irq(BUTTON_IRQ, button_interrupt, IRQ_TYPE_EDGE_BOTH,DEV_NAME, NULL)) { printk(KERN_ERR"cannotallocate irq"); return- EBUSY; } …… INIT_WORK(&button_wq,work_handler); printk("initialized\n"); return0; }
初始化函数主要完成了两个工作:
1、 申请中断
2、 创建一个推后的工作button_wq,有两种方法:
动态创建:
INIT_WORK(struct work_struct work, void (*func)(void *)), 在新内核里,INIT_WORK已经发生了变化,少了第三个参数。可以参考http://hi.baidu.com/amokefei/blog/item/db998b2e1885a134d52af13e.html
静态创建:
DECLARE_WORK(name,func) 创建一个名为name,处理函数为func的work_struct结构体。
不管是动态还是静态,都是填充work struct 结构体。
有兴趣可以去看内核里gpio_keys.c的实现代码,一个典型的work queue的实现例子。