如何使用Linux工作队列workqueue
本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性。
参考资料:《Linux设备驱动程序》第3版 LDD3e, LKD3e, 《 Linux per-CPU实现分析 》,linux-2.6.27,irq_balance
2,使用工作队列
*编译时静态创建,因为暂时用不到也就没去看具体实现过程和用法。
#define DECLARE_WORK(name, void(*func)(void *), void *data);
其使用的默认处理函数为work_handler(void *data)。
使用schedule_work(&work)进行工作调度。
*动态创建
INIT_WORK(struct work_struct *work,void(*func)(void *), void *data);
比如如下代码
static DEFINE_PER_CPU(struct sk_buff_head, bs_cpu_queues);
int netif_receive_skb(struct sk_buff *skb)//接收到网卡数据
{
……
……
static inline int bs_dispatch(struct sk_buff *skb)//分发网卡数据
{
struct sk_buff_head *q;
q = &per_cpu(bs_cpu_queues, cpu);//从CPU为cpu处取一个sk_buff_head数据结构
//把数据skb插入到双向循环链表q中。这样子这个q就是待处理的数据了。
bs_works = &per_cpu(bs_works, cpu);//从CPU为cpu处取一个work_struct结构
if (!bs_works->func) {//假如当前工作处理函数指针为空
INIT_WORK(bs_works, bs_func, q);//创建工作队列,工作队列函数指为bs_func,处理的数据为q。
queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), bs_works);//在CPU编号为cpu的默认队列keventd_wq中插入一个bs_works工作任务。具体看下面。
}
}
……
……
}
3,创建新的工作队列已经在《Linux工作队列workqueue实现分析》讲过了,是通过create_workqueue实现,这里不再重复。
4,工作调度
*默认工作队列处理函数
void work_handler(void *data)//在好几个版本的内核里没有发现这个函数或者宏定义,只找到了work_handlers,它其实是个函数指针数组,具体实现没仔细看,大概就是为初始化一些函数,再看时机进行调度。
使用schedule_work()对默认的event队列进程调度。
*调度新创建的工作队列
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
从其参数可知,它是对某种类型的任务进行工作调度,即这种类型的每个CPU中的工作者线程的每个体work_struct都要被调度。
由于上面使用的是per_cpu_ptr(keventd_wq->cpu_wq, cpu),其返回的是CPU为cpu的keventd_wq->cpu_wq workqueue_struct结构,即默认的工作队列,所以上面其实可以使用schedule_work(&work)进行调度。使用queue_work一般是自己指定自行创建的工作队列wq,这个工作队列由 create_workqueue()创建返回。