工作队列是内核用于进行延后工作的一种方式。当然驱动模块中使用之前提到的kernel thread就可以完成延后工作了,但是如果每个驱动模块都创建自己的kernel thread,那么内核线程数量过多,这会影响整体的性能。因此,最好的方法就是把这些需求汇集起来,提供一个统一的机制,也就是workqueue了。
它的结构是workqueue_struct;
和工作队列息息相关的是工作元素work_struct,毕竟队列只是一个容器罢了,光有队列是不能干什么活的,它定义在kernel-4.9/include/linux/workqueue.h
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
work_struct的初始化,定义在kernel-4.9/include/linux/workqueue.h中;这个方法的用途是将对应的work_struct变量和函数地址建立联系。
#define INIT_WORK(_work, _func) \
__INIT_WORK((_work), (_func), 0)
这里有一个比较奇怪的地方,因为一些资料里面提到的INIT_WORK方法有第三个参数data,然而在kerenl-4.9下的并没有,于是再查看一了下kernel-3.18的代码;
#define INIT_WORK(_work, _func) \
do { \
__INIT_WORK((_work), (_func), 0); \
} while (0)
虽然有些不同,但是,依然没有第三个参数。感觉应该早前版本的形式,如2.6之类的。
通常来说在初始化之前,得先分配个内存;
struct work_struct *hardwork;
hardwork = kamlloc(sizeof(struct work_struct), GFP_KERNEL);
INIT_WORK(hardwork, func);
在真正使其工作前,还得把工作队列给初始化了:
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
#define create_freezable_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \
WQ_MEM_RECLAIM, 1, (name))
#define create_singlethread_workqueue(name) \
alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)
create_singlethread_workqueue创建工作队列即使对于多CPU系统,内核也只负责在一个cpu上创建一个worker_thread内核线程;而使用create_workqueue创建工作队列对于多CPU系统,内核将会在每个CPU上创建一个worker_thread内核线程,使得线程处理的事务能够并行化。
用完之后得回收,下面是用于销毁workqueue的函数;
extern void destroy_workqueue(struct workqueue_struct *wq);
有了队列和元素,下一步就是把元素提交给队列;
/**
* queue_work - queue work on a workqueue
* @wq: workqueue to use
* @work: work to queue
*
* Returns %false if @work was already on a queue, %true otherwise.
*
* We queue the work to the CPU on which it was submitted, but if the CPU dies
* it can be processed by another CPU.
*/
static inline bool queue_work(struct workqueue_struct *wq,
struct work_struct *work)
{
return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}
/**
* schedule_work - put work task in global workqueue
* @work: job to be done
*
* Returns %false if @work was already on the kernel-global workqueue and
* %true otherwise.
*
* This puts a job in the kernel-global workqueue if it was not already
* queued and leaves it in the same position on the kernel-global
* workqueue otherwise.
*/
static inline bool schedule_work(struct work_struct *work)
{
return queue_work(system_wq, work);
}
最后就可以坐等work_struct中关联的函数被调用了