简介
讨论了 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 kworker
5 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.c
NR_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
- 此标志会将队列分为绑定和非绑定队列两种形式
```c
include/linux/workqueue.h
WQ_UNBOUND
WQ_FREEZABLE
WQ_MEM_RECLAIM
WQ_HIGHPRI
WQ_CPU_INTENSIVE
WQ_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讲解