2016年(7)
分类: 嵌入式
2016-11-08 15:21:00
原文地址:(每天笔记)arm驱动之工作队列 作者:创城
1 .工作队列概述
工作队列(work
queue)是另外一种将工作推后执行的形式,它和我们前面讨论的所有其他形式都不相同。工作队列可以把工作推后,交由一个内核线程去执行—这个下半部分总是会在进程上下文执行,但由于是内核线程,其不能访问用户空间。最重要特点的就是工作队列允许重新调度甚至是睡眠。
通常,在工作队列和软中断/tasklet中作出选择非常容易。可使用以下规则:
? 如果推后执行的任务需要睡眠,那么只能选择工作队列;
? 如果推后执行的任务需要延时指定的时间再触发,那么使用工作队列,因为其可以利用timer延时;
? 如果推后执行的任务需要在一个tick之内处理,则使用软中断或tasklet,因为其可以抢占普通进程和内核线程;
? 如果推后执行的任务对延迟的时间没有任何要求,则使用工作队列,此时通常为无关紧要的任务。
另外如果你需要用一个可以重新调度的实体来执行你的下半部处理,你应该使用工作队列。它是惟一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在你需要获得大量的内存时、在你需要获取信号量时,在你需要执行阻塞式的I/O操作时,它都会非常有用。
实际上,工作队列的本质就是将工作交给内核线程处理,因此其可以用内核线程替换。但是内核线程的创建和销毁对编程者的要求较高,而工作队列实现了内核线程的封装,不易出错,所以我们也推荐使用工作队列。
1. 创建
2. 提交队列
3. 删除
1.自定义队列
创建:
创建分为工作队列的创建和工作函数(任务)的创建.
(1) 工作队列的创建需要有其描述符,它的数据结构是 workqueue_struct.该结构定义在中.这里我们不需要关心它的具体组成,内核已经写好了两个两个函数帮我们创建队列:
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct * workqueue_singlethread_workqueue(const char *name);
他们的区别在于实际处理器的多少,如果是单核处理器的话,他们毫无区别.
因为每个工作队列都有一个或多个(多核处理器)专用的进程(内核线程),这些进程运行提交到该工作队列函数.
create_workqueue内核会在系统中的每个处理器上为该工作队列创建专用的线程.这样,如果工作队列足够多的话,可能对系统的性能有所杀伤,而create_singlethread_workqueue则只会创建一个专用的线程.所以,如果单个工作线程足够使用,推荐使用第二个函数来创建工作队列.
同样的,工作任务的创建也需要有其描述符,它的数据结构是work_struct.内核同样为我们创建好了几个宏来方便的创建它.
DECLARE_WORK(name,void(*function)(void *),void *data); 静态定义的一个工作
用于在内核编译时使用.
INIT_WORK(struct work_struct *work,void(*function)(void *),void *data); 动态定义一个工作
用于在系统运行时创建.首次创建时使用它.
PREPARE_WORK(struct work_struct *work,void(*function)(void *),void *data);
用于在系统运行时创建.没有INIT_WORK初始化彻底,因为它不会初始化用来将work_struct结构连接到工作队列的指针.如果结构已经被提交到工作队列,而只是需要修改该结构,则应该使用PREPARE_WORK而不是INIT_WORK.
提交:
如果要将工作提交到工作队列,则可使用如下两个函数之一:
int queue_work(struct workqueue_struct *queue,struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue,struct work_struct *work,unsigned long delay);
flush_workqueue(struct workqueue_struct *wq) 刷新等待指定队列中的所有工作完成
它们都会将work添加到给定的queue.但是如果使用queue_delayed_work,则实际工作至少会在经过指定的jiffies(由delay指定)之后才会执行.如果工作被成功添加到队列,则上述函数的返回值为1,返回值为非零意味着给定的work_struct结构已经等待在该队列中,从而不能两次加入该队列.
删除:
结束对工作队列的使用后,可调用下面的函数释放相关资源.
void destroy_workqueue(struct workqueue_struct *queue);
2.内核提供的共享队列
创建:
INIT_WORK(struct work_struct *work,void(*function)(void *),void *data);
提交:
int schedule_work(struct work_struct *work)
{
return queue_work(keventd_wq, work);
}
#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
static struct workqueue_struct *queue = NULL;
static struct work_struct work;
static void work_handler(struct work_struct *data)
{
printk(KERN_ALERT “work handler function.\n”);
}
static int __init test_init(void)
{
queue = create_singlethread_workqueue(“helloworld”); /*创建一个单线程的工作队列*/
if (!queue)
goto err;
INIT_WORK(&work, work_handler);
queue_work(queue, &work);
return 0;
err:
return -1;
}