简介
讨论了 tasklet的使用,现在我们来讨论一下工作队列的使用。和task不同的是,Workqueue 可睡眠,有延迟。
而且比tasklet复杂。工作队列与任务小程序一样,用于延迟处理中断(但我们也可以将其用于其他目的),工作队列的大致工作流程,Tasklet由调度功能处理,工作队列由称为工作程序(workqueue)的特殊线程处理。 workqueue的主要设计思想:一个是并行,多个work不要相互阻塞;另外一个是节省资源,多个work尽量共享资源(进程、调度、内存),不要造成系统过多的资源浪费。

我们先来描述几个工作队列中使用到的名词。
- work :任务。
- workqueue :任务的集合(队列)。workqueue和work是一对多的关系。
- worker :执行任务的线程,在代码中worker对应一个work_thread()内核线程。
- worker_pool:线程的集合。worker_pool和worker是一对多的关系。
- pwq(pool_workqueue):中间人/中介,负责建立起workqueue和worker_pool之间的关系。workqueue和pwq是一对多的关系,pwq和worker_pool是一对一的关系。
[root@imx6ull:~]# ps|grep kworker5 root [kworker/0:0H]6 root [kworker/u2:0]110 root [kworker/0:3]127 root [kworker/u2:2]
worker_pool
首先执行work的线程我们称作worker,多个worker集合在一起称作 worker_pool。 worker_pool可以分为两种绑定队列(bound queues)和非绑定队列(unbound queues)。
- 绑定队列只能在当前核心上面执行
- 非绑定队列可以在多有核心上面运行
bound queues
这个类型的线程是作为默认创建的worker模板。系统会给每一个CPU(核心)创建 NR_STD_WORKER_POOLS个pool。其两个pools的默认nice为** 0** 以及 HIGHPRI_NICE_LEVEL。在添加过程中将作品绑定到当前CPU。因此,在这种队列中,工作是在对其进行调度的核心上执行的。在这方面,绑定队列类似于任务集。kernel/workqueue.cNR_STD_WORKER_POOLS = 2, /* # standard pools per cpu */static int __init init_workqueues(void)int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL };
unbound worker_pool
这个类型的线程可以在任何核心上运行。
**
使用workqueue的大致步骤
首先使用 INIT_WORK 定义workqueue,其次使用 shudule_work或是queue_work 来激活这一个workqueue。
值得注意的是即使使用queue_work等激活了这个work。但还是不会马上运行。需要等到你绑定的事件触发以后,就可有运行了。创建队列
```c include/linux/workqueue.hdefine alloc_workqueue(fmt, flags, max_active, args…) \
__alloc_workqueue_key((fmt), (flags), (max_active), \NULL, NULL, ##args)
调用例程
rxrpc_workqueue = alloc_workqueue(“krxrpcd”, 0, 1); l2tp_wq = alloc_workqueue(“l2tp”, WQ_UNBOUND, 0);
- fmt和args参数是名称和参数的printf格式。- max_activate 表示可以同时从这个队列中的单个CPU上执行工作的最大数量。- 下面是另外的一些可变参数<a name="DgRKs"></a>### 创建标志的讲解- WQ_UNBOUND- 此标志会将队列分为绑定和非绑定队列两种形式```cinclude/linux/workqueue.hWQ_UNBOUNDWQ_FREEZABLEWQ_MEM_RECLAIMWQ_HIGHPRIWQ_CPU_INTENSIVEWQ_SYSFS
创建域销毁
include/linux/workqueue.h#define DECLARE_WORK(n, f) \struct work_struct n = __WORK_INITIALIZER(n, f)#define DECLARE_DELAYED_WORK(n, f) \struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, 0)#define DECLARE_DEFERRABLE_WORK(n, f) \struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, TIMER_DEFERRABLE)
简单的demo
#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/workqueue.h>struct workqueue_struct *workqueue_test;struct work_struct work_test;void work_test_func(struct work_struct *work) { printk("%s()\n", __func__); }static int test_init(void) {printk("Hello,world!\n");/* 1. 自己创建一个workqueue, 中间参数为0,默认配置 */workqueue_test = alloc_workqueue("workqueue_test", 0, 0);/* 2. 初始化一个工作项,并添加自己实现的函数 */INIT_WORK(&work_test, work_test_func);/* 3. 将自己的工作项添加到指定的工作队列去, 同时唤醒相应线程处理 */queue_work(workqueue_test, &work_test);return 0;}static void test_exit(void) {printk("Goodbye,cruel world!\n");destroy_workqueue(workqueue_test);}module_init(test_init);module_exit(test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("zhouchengzhu <1073355312@qq.com>");MODULE_DESCRIPTION("A simple workqueue driver");MODULE_VERSION("2:1.0");
参考资料
Linux Workqueue
Introduction to deferred interrupts (Softirq, Tasklets and Workqueues)
Multitasking Management in the Operating System Kernel
Multitasking in the Linux Kernel. Workqueues
workqueue —最清晰的讲解
Linux-workqueue讲解
