全部博文(573)
分类: LINUX
2015-12-09 15:46:21
一、工作项、工作队列和工作者线程
把推后执行的任务叫做工作(work),描述它的数据结构为work_struct ,这些工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct ,而工作线程就是负责执行工作队列中的工作。系统默认的工作者线程为events。
工作队列(work queue)是另外一种将工作推后执行的形式。工作队列可以把工作推后,交由一个内核线程去执行—这个下半部分总是会在进程上下文执行,但由于是内核线程,其不能访问用户空间。最重要特点的就是工作队列允许重新调度甚至是睡眠。
通常,在工作队列和软中断/tasklet中作出选择非常容易。可使用以下规则:
如果推后执行的任务需要睡眠,那么只能选择工作队列;
如果推后执行的任务需要延时指定的时间再触发,那么使用工作队列,因为其可以利用timer延时;
如果推后执行的任务需要在一个tick之内处理,则使用软中断或tasklet,因为其可以抢占普通进程和内核线程;
如果推后执行的任务对延迟的时间没有任何要求,则使用工作队列,此时通常为无关紧要的任务。
实际上,工作队列的本质就是将工作交给内核线程处理,因此其可以用内核线程替换。但是内核线程的创建和销毁对编程者的要求较高,而工作队列实现了内核线程的封装,不易出错,所以我们也推荐使用工作队列。
二、工作队列使用
相关文件:
kernel/include/linux/workqueue.h
Kernel/kernel/workqueue.c
要使用工作队列,需要先创建工作项,有两种方式:
静态创建:
DECLARE_WORK(name, function); 定义正常执行的工作项
DECLARE_DELAYED_WORK(name, function); 定义延后执行的工作项
eg:
@ kernel/driver/input/keyboard/mt6516_kpd.c
@ mtk/src/custom/common/kernel/touchpanel/st1332/driver.c
static void kpd_switch_backlight(struct work_struct *work);
static DECLARE_WORK(kpd_backlight_work, kpd_switch_backlight);
static void st1332_kpd_switch_backlight(struct delayed_work *work);
static DECLARE_DELAYED_WORK(kpd_backlight_work, st1332_kpd_switch_backlight);
动态创建,运行时创建:
eg:
@ kernel/driver/input/touchscreen/tspad.c
static struct work_struct work;
struct delayed_work led_work;
static void new_ts_work(struct work_struct *work);
static void s0340_ledtime_scanf(unsigned long data);
通常在probe()函数中执行下面的操作来初始化工作项:
INIT_WORK(&work, new_ts_work);
INIT_DELAYED_WORK(&led_work, s0340_ledtime_scanf);
工作队列待执行的函数原型是:
typedef void (*work_func_t)(struct work_struct *work);
这个函数会由一个工作者线程执行,因此,函数会运行在进程上下文中。默认情况下,允许响应中断,并且不持有任何锁。如果需要,函数可以睡眠。需要注意的是,尽管该函数运行在进程上下文中,但它不能访问用户空间,因为内核线程在用户空间没有相关的内存映射。通常在系统调用发生时,内核会代表用户空间的进程运行,此时它才能访问用户空间,也只有在此时它才会映射用户空间的内存。
创建了工作项之后,在适当的时候可以通过下面的两种方式来提交工作项给工作者线程,通常我们使用的工作队列和工作者线程都是系统初始化时候默认创建的。
schedule_work(&work) ;
&work马上就会被调度,一旦其所在的处理器上的工作者线程被唤醒,它就会被执行。
schedule_delayed_work(&delay_work, delay);
&delay_work指向的 delay_work 直到 delay 指定的时钟节拍用完以后才会执行。
eg :
schedule_delayed_work(&kpd_backlight_work, msecs_to_jiffies(300));