上一篇博客介绍了tasklet实现中断下半部,接下来介绍工作队列实现中断下半部。相对于软中断/tasklet,工作队列运行在进程上下文,允许睡眠。
一、工作队列的使用
1、定义并初始化工作队列
创建工作队列函数:
struct workqueue_struct *create_workqueue(const char *name)
函数传参是内核中工作队列的名称,返回值是workqueue_struct结构体的指针,该结构体用来维护一个等待队列。
如:
struct workqueue *heql_wq; //定义工作队列
heql_wq = create_workqueue(“heql”);
2、定义并初始化work结构体
内核使用结构体来维护一个加入工作队列的任务:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; //下半部实现的处理函数指针
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
有静态和动态两种方法:
(1)静态初始化
#define DECLARE_WORK(n, f) \
struct work_struct n = __WORK_INITIALIZER(n, f)
定义并初始化一个叫n的work_struct结构体,它对应的处理函数是f。
如:
void my_func(struct work_struct *work)
{
......
}
DECLARE_WORK(mystruct, my_func);
(2)动态初始化
如:
struct work_struct heql_work; //定义work结构体
void heql_func(struct work_struct *work) //处理函数
{
......
}
INT_WORK(&heql_work, heql_func); //初始化work结构体
3、调度任务
/*kernel/workqueue.c*/
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
将指定的任务(work_struct),添加到指定的工作队列中。调度并不代表处理函数能够马上执行,这由内核进程调度决定。
4、卸载模块时,刷新并注销等待队列
刷新等待队列:
void flush_workqueue(struct workqueue_struct *wq)
该函数会一直等待,直到指定的等待队列中所有的任务都执行完毕并从等待队列中移除。
注销等待队列:
void destroy_workqueue(struct workqueue_struct *wq)
例子:
#include<...>
struct workqueue_struct *heql_wq; //定义工作队列
struct work_struct heql_work; //定义work结构体
void heql_func(struct work_struct *work) //处理函数
{
......
}
irqreturn_t irq_handler(int irq, void *dev_id)
{
......
queue_work(heql_wq, &heql_work); //调度任务
return IRQ_HANDLED;
}
static int __init test_init(void) //模块初始化
{
......
heql_wq = create_workqueue(“heql”); //初始化工作队列
INIT_WORK(&heql_work, heql_func); //初始化work结构体
......
}
static void __exit test_exit(void) //模块卸载
{
flush_workqueue(heql_wq); //刷新工作队列
destroy_workqueue(heql_wq); //注销工作队列
......
}
二、使用共享的工作队列
在内核中有一个默认的工作队列events,使用共享的工作队列可以省去创建和注销工作队列的步骤。如果有多个不同的任务都加到这个工作队列中,每个任务调度的速度就会比较慢。一般默认工作队列都能满足需求,不需要创建一个新的工作队列。
共享工作队列的使用:
1、实现处理函数,定义并初始化work结构体
这里和之前所说的步骤一样。
2、调度任务
默认工作队列中的任务的调度不需要指定工作队列,函数如下:
/*kernel/workqueue.c*/
int schedule_work(struct work_struct *work)
该函数会把work_struct结构体加入到默认工作队列events中。
例子:
#include<linux/workqueue.h>
void myfunc(struct work_struct *work)
{
......
}
DECLARE_WORK(mywork, func); //定义并初始化work结构体
static irqreturn_t handler(int irq, void *ptr)
{
......
schedule_work(&mywork);
returnt IRQ_HANDLED;
}
三、总结
以上介绍了工作队列的使用,除此之外,还有函数能够使工作队列的任务延时执行,相关的结构体和函数有:
1、struct delayed_work{}
2、DECLARE_DELAYED_WORK(n,f)
3、INIT_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
4、int queue_delayed_work(struct workqueue_struct *queue,
struct delayed_work *work, unsigned long delay);
5、schedule_delayed_work(struct delayed_work *work, unsigned long delay)