queue_work 是 Linux 内核中 workqueue 机制的一个关键函数,用于将工作(work)提交到工作队列(workqueue)中异步执行。
workqueue 基本概念
workqueue 是 Linux 内核中自 2.6 版本引入的一种任务执行机制,与 softirq 和 tasklet 并称为下半部(bottom half)三剑客[1]。与其他下半部机制不同,workqueue 在进程上下文中执行,这意味着它可以睡眠,这是它的一个重要特性[2]。
workqueue 的核心思想是分离任务(work)的发布者和执行者[1]。当需要执行任务时,构造一个 work,通过 queue_work 函数将其放入相应的 workqueue,由 workqueue 所绑定的 worker 线程(实际上是内核线程)去执行。
queue_work 工作原理
queue_work 函数的主要工作是将一个 work 结构体添加到指定的工作队列中,并通知相应的 worker 线程来处理它。其基本流程如下:
-
检查 work 的状态:首先通过 test_and_set_bit 原子操作检查 WORK_STRUCT_PENDING_BIT 位,确保该 work 不会被重复添加[2][5]。
-
调用 __queue_work:如果 work 不处于 pending 状态,则调用 __queue_work 函数将 work 挂入工作队列并通知 worker 线程池来处理[5]。
-
唤醒 worker 线程:queue_work 会调用 wake_up 来唤醒休眠在 cwq->more_work 上的 worker_thread 进程[3]。
工作队列的处理流程
当 work 被添加到工作队列后,worker 线程的处理流程大致如下:
- 从该 worker 线程对应的 work list 中摘下一个 work[2]。
- 清除 pending 标志[2]。
- 执行该 work 的回调函数(callback function)[2]。
并发处理
在 2.6.36 版本之前,workqueue 和 worker 的对应关系是二元化的:要么使用 Multi Threaded (MT) workqueue 每个 CPU 一个 worker,要么使用 Single Threaded (ST) workqueue 整个系统只有一个 worker[1]。
在 2.6.36 版本中引入了 CMWQ(Concurrency Managed Workqueue)机制,通过引入 worker pool 概念,实现了对 workqueue 机制中生产者(workqueue)和消费者(worker)的解耦[1]。
对于 single thread workqueue,由于只有一个 worklist 和一个线程,work 的回调函数是严格串行执行的。而对于 per cpu workqueue,各个 CPU 上都有一个 worker 线程及其要处理的 work list,因此可能会有多个 work 在多个 CPU 上并行执行[2]。
使用示例
使用 workqueue 的基本步骤如下:
-
创建或获取一个工作队列:
struct workqueue_struct *wq = create_workqueue("my_queue");
或使用系统默认的工作队列
system_wq
。 -
定义并初始化一个 work 结构体:
DECLARE_WORK(my_work, my_work_handler);
或动态初始化:
INIT_WORK(&my_work, my_work_handler);
-
提交 work 到工作队列:
queue_work(wq, &my_work);
或指定 CPU:
queue_work_on(cpu, wq, &my_work);
通过这种方式,可以将耗时的工作推迟到工作队列中异步执行,避免阻塞当前执行路径。