目录
一、从现实到内核:工作队列的快递站模型
1.1 工作队列与工作池
在许多场景下,我们都需要一个能够异步执行程序的环境。工作队列(workqueue)API 是实现这种异步执行环境最常用的方式。通过将待办任务(工作项)放入队列,并由独立的线程(工作者)来执行队列中的任务,实现程序的异步执行。
工作池(worker_pool):是一组预先创建好的工作者线程(worker thread)的集合。这些工作线程处于等待状态,随时准备执行分配给它们的工作项(work item)。工作池的主要作用是提高系统效率,避免在需要执行工作时频繁创建和销毁线程所带来的开销。具有以下特点和作用:
- 资源复用:工作池中的工作线程可以被重复使用,处理多个不同的工作项。这样可以减少线程创建和销毁的开销,提高系统性能。
- 负载均衡:工作池可以根据系统的负载情况,合理地分配工作项给不同的工作线程,从而实现负载均衡。
- 并发执行:工作池中的多个工作线程可以并发地执行工作项,提高系统的并发处理能力。
工作队列(workqueue):工作队列是一种内核机制,用于在进程上下文(而非中断上下文)中执行延迟工作。用户可以将需要延迟执行的工作项添加到工作队列中,内核会在合适的时机将这些工作项分配给工作池中的工作线程去执行。具有以下特点和作用:
- 延迟执行:工作队列允许将工作项的执行延迟到合适的时机,避免在关键代码路径中执行耗时的操作,从而提高系统的响应性能。
- 进程上下文执行:工作队列中的工作项在进程上下文执行,这意味着它们可以睡眠、访问用户空间等,而中断处理程序则不能。
- 管理工作项:工作队列负责管理工作项的添加、删除和调度,确保工作项能够按照正确的顺序和时机被执行。
工作队列和工作池是紧密相关的,工作队列本身并不直接执行工作,而是将工作项分发给对应的工作池。每个工作队列可以关联一个或多个工作池,工作队列会根据工作项的属性(如是否绑定到特定 CPU)选择合适的工作池来执行工作。
1.2 以快递站为例理解
想象你正在经营一家24小时营业的快递站(内核空间),每天需要处理成千上万个包裹(工作任务)。这些包裹有的需要立即派送(中断处理),有的可以稍后处理(延迟任务),还有的需要特殊车辆运输(特定CPU核心处理)。
传统处理方式的困境:
- 直接让快递员(中断处理程序)送货到凌晨3点?→ 违反劳动法(可能影响实时性)
- 所有包裹堆在老板办公室(共享队列)?→ 找件效率低下(锁竞争)
内核工作队列的智慧:
- 专业派送团队:创建专属快递员(内核线程),工作池就是快递站
- 智能分拣系统:自动分配包裹到不同区域(不同工作队列)
- 弹性工作时间:空闲时多送件,忙时动态扩容(并发管理)
二、解剖工作队列的"五脏六腑"
2.1 核心数据结构一览
2.1.1 struct work_struct
// 包裹运单(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
};
atomic_long_t data
:
- 功能:这是一个原子类型的长整型变量,用于存储与工作项相关的数据。通常,驱动开发者会将一个指针(如指向设备结构体的指针)存储在这个成员中,以便在工作函数执行时可以访问相关的数据。
- 用途:在工作函数中,可以通过
data
成员获取额外的上下文信息。例如,在设备驱动中,可以将设备结构体的指针存储在data
中,这样工作函数就可以操作该设备。
struct list_head entry
:
- 功能:这是一个双向链表节点,用于将
work_struct
实例连接到工作队列的链表中。工作队列使用链表来管理所有待执行的工作项。 - 用途:内核通过操作这个链表节点,将工作项添加到工作队列中,或者从工作队列中移除已经完成的工作项。
work_func_t func
:
- 功能:这是一个函数指针,指向实际要执行的工作函数。当工作项被调度执行时,内核会调用这个函数。
- 用途:驱动开发者需要定义一个符合
work_func_t
类型的函数,并将其赋值给func
成员。这个函数就是工作项要执行的具体任务,例如处理设备的中断、更新设备状态等。work_func_t
的定义通常如下:
typedef void (*work_func_t)(struct work_struct *work);
struct lockdep_map lockdep_map
:
- 功能:这是一个条件编译的成员,只有在启用了
CONFIG_LOCKDEP
配置选项时才会包含。lockdep
是 Linux 内核中的一个锁验证机制,用于检测锁的使用是否正确,避免死锁等问题。lockdep_map
结构体用于记录工作项的锁依赖信息。 - 用途:在调试和开发过程中,启用
CONFIG_LOCKDEP
可以帮助开发者发现和解决锁相关的问题。但在生产环境中,为了减少开销,通常会禁用这个选项。
2.1.2 struct delayed_work
/**
* struct delayed_work - 延迟执行的工作结构体
*
* 该结构体用于表示一个可以延迟执行的工作任务,包含了一个普通工作项和一个定时器。
*
* 成员说明:
* @work: 普通工作项,定义了实际需要执行的任务函数。
* @timer: 定时器,用于控制工作项的延迟执行时间。
* @wq: 目标工作队列,指定该延迟工作将被加入的工作队列。
* @cpu: 目标CPU,指定该延迟工作将在哪个CPU上执行。
*
* 注意:ANDROID_KABI_RESERVE字段为Android KABI保留字段,具体用途由平台定义。
*/
struct delayed_work {
struct work_struct work; // 普通工作项,包含任务函数等信息
struct timer_list timer; // 定时器,用于延迟执行
/* 目标工作队列和CPU,定时器使用这些信息来排队工作项 */
struct workqueue_struct *wq; // 目标工作队列
int cpu; // 目标CPU
ANDROID_KABI_RESERVE(1); // Android KABI保留字段1
ANDROID_KABI_RESERVE(2); // Android KABI保留字段2
};
2.1.3 struct rcu_work
/**
* 结构体rcu_work用于在RCU(Read-Copy-Update)环境中安排工作项
* 它结合了工作队列机制和RCU机制,以实现高效的异步操作和内存管理
*/
struct rcu_work {
// work_struct用于将工作项放入工作队列中,以便在适当的时机执行
struct work_struct work;
// rcu_head用于将结构体安全地释放到RCU机制中,以延迟回收
struct rcu_head rcu;
/* target workqueue ->rcu uses to queue ->work */
// wq指向目标工作队列,RCU机制使用它来将work_struct结构体排入队列
struct workqueue_struct *wq;
};
2.1.4 struct workqueue_struct
// 快递站档案(kernel/workqueue.c)
struct workqueue_struct {
struct list_head pwqs; /* WR: all pwqs of this wq */
struct list_head list; /* PR: list of all workqueues*/
struct mutex mutex; /* protects this wq */
int work_color; /* WQ: current work color */
int flush_color; /* WQ: current flush color */
atomic_t nr_pwqs_to_flush; /* flush in progress */
struct wq_flusher *first_flusher; /* WQ: first flusher */
struct list_head flusher_queue; /* WQ: flush waiters */
struct list_head flusher_overflow; /* WQ: flush overflow list */
struct list_head maydays; /* MD: pwqs requesting rescue */
struct worker *rescuer; /* MD: rescue worker */
int nr_drainers; /* WQ: drain in progress */
int saved_max_active; /* WQ: saved pwq max_active */
struct workqueue_attrs *unbound_attrs; /* PW: only for unbound wqs */
struct pool_workqueue *dfl_pwq; /* PW: only for unbound wqs */
#ifdef CONFIG_SYSFS
struct wq_device *wq_dev; /* I: for sysfs interface */
#endif
#ifdef CONFIG_LOCKDEP
char *lock_name;
struct lock_class_key key;
struct lockdep_map lockdep_map;
#endif
char name[WQ_NAME_LEN]; /* I: workqueue name */
/*
* Destruction of workqueue_struct is RCU protected to allow walking
* the workqueues list without grabbing wq_pool_mutex.
* This is used to dump all workqueues from sysrq.
*/
struct rcu_head rcu;
/* hot fields used during command issue, aligned to cacheline */
unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */
struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */
struct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */
};
链表相关成员:
struct list_head pwqs; /* WR: all pwqs of this wq */
struct list_head list; /* PR: list of all workqueues */
pwqs
:是一个双向链表头,用于连接该工作队列所有的pool_workqueue
实例。pool_workqueue
是工作队列与工作线程池之间的桥梁,它将工作项分发给具体的工作线程池。list
:用于将所有的workqueue_struct
实例连接成一个全局链表,方便内核管理和遍历所有的工作队列。
互斥锁和颜色相关成员:
struct mutex mutex; /* protects this wq */
int work_color; /* WQ: current work color */
int flush_color; /* WQ: current flush color */
atomic_t nr_pwqs_to_flush; /* flush in progress */
struct wq_flusher *first_flusher; /* WQ: first flusher */
struct list_head flusher_queue; /* WQ: flush waiters */
struct list_head flusher_overflow; /* WQ: flush overflow list */
mutex
:一个互斥锁,用于保护该工作队列的相关操作,防止多个线程同时修改工作队列的状态。work_color
和flush_color
:颜色标记,用于实现工作队列的同步和刷新机制。不同颜色可以区分不同阶段的工作。nr_pwqs_to_flush
:一个原子变量,记录当前正在进行刷新操作的pool_workqueue
数量。first_flusher
:指向第一个刷新器的指针,刷新器负责清空工作队列中的工作项。flusher_queue
:一个双向链表,用于存储等待刷新工作队列的元素。flusher_overflow
:溢出链表,用于处理刷新过程中的溢出情况。
救援相关成员:
struct list_head maydays; /* MD: pwqs requesting rescue */
struct worker *rescuer; /* MD: rescue worker */
maydays
:双向链表,用于连接那些请求救援的pool_workqueue
实例。当某个工作线程池出现问题时,会将其添加到这个链表中。rescuer
:指向救援工作线程的指针,救援工作线程负责处理那些请求救援的工作线程池。
排水和保存最大活动数相关成员:
int nr_drainers; /* WQ: drain in progress */
int saved_max_active; /* WQ: saved pwq max_active */
nr_drainers
:记录当前正在进行排水操作(即清空工作队列)的数量。saved_max_active
:保存的pool_workqueue
的最大活动工作项数量,用于在某些情况下恢复工作队列的状态。
未绑定工作队列相关成员:
struct workqueue_attrs *unbound_attrs; /* PW: only for unbound wqs */
struct pool_workqueue *dfl_pwq; /* PW: only for unbound wqs */
unbound_attrs
:指向工作队列属性的指针,仅用于未绑定的工作队列。未绑定的工作队列可以在不同的 CPU 节点上执行工作。dfl_pwq
:指向默认的pool_workqueue
的指针,仅用于未绑定的工作队列。
可选配置相关成员:
#ifdef CONFIG_SYSFS
struct wq_device *wq_dev; /* I: for sysfs interface */
#endif
#ifdef CONFIG_LOCKDEP
char *lock_name;
struct lock_class_key key;
struct lockdep_map lockdep_map;
#endif
wq_dev
:仅在启用CONFIG_SYSFS
配置选项时存在,用于提供工作队列的 sysfs 接口,方便用户空间程序与工作队列进行交互。lock_name
、key
和lockdep_map
:仅在启用CONFIG_LOCKDEP
配置选项时存在,用于锁依赖检测机制,帮助调试和发现锁使用中的问题。
工作队列名称和 RCU 相关成员:
char name[WQ_NAME_LEN]; /* I: workqueue name */
struct rcu_head rcu;
name
:存储工作队列的名称,方便内核调试和管理。rcu
:RCU(Read-Copy-Update)机制的头部,用于实现工作队列的 RCU 保护。RCU 允许在不持有锁的情况下安全地遍历工作队列链表。
热字段相关成员:
unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */
struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */
struct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */
flags
:工作队列的标志位,使用WQ_*
宏定义,用于控制工作队列的行为。____cacheline_aligned
确保该成员与缓存行对齐,提高访问效率。cpu_pwqs
:指向每个 CPU 对应的pool_workqueue
实例的指针数组,用于处理绑定到特定 CPU 的工作队列。numa_pwq_tbl
:一个 RCU 保护的数组,用于存储未绑定工作队列在不同 NUMA 节点上的pool_workqueue
实例。
2.2 使用工作队列的API接口
2.2.1 初始化工作项
INIT_WORK
:初始化一个已有的工作项。
#define INIT_WORK(_work, _func) \
__INIT_WORK((_work), (_func), 0)
- 初始化工作项:将
struct work_struct
的成员初始化为指定的函数,使其能够被工作队列调度执行。 - 参数说明:
_work
:指向struct work_struct
的指针,表示要初始化的工作项。_func
:要执行的函数指针(类型为void (*func)(struct work_struct *)
)。0
:第三个参数(隐含传递)表示不设置任何标志位(如TIMER_DEFERRABLE
),说明这是一个普通工作项(非延迟或可延迟类型)。
示例:
#include <linux/workqueue.h>
// 工作函数
void my_work_func(struct work_struct *work)
{
printk(KERN_INFO "My work function is running.\n");
}
// 定义并初始化工作项
struct work_struct work;
INIT_WORK(my_work, my_work_func);
INIT_WORK_ONSTACK
:动态初始化一个工作项,使用栈上内存。
#define INIT_WORK_ONSTACK(_work, _func) \
__INIT_WORK((_work), (_func), 0)
INIT_DELAYED_WORK
:初始化一个延迟工作项,可用于在一段时间后执行工作。
#define INIT_DELAYED_WORK(_work, _func) \
__INIT_DELAYED_WORK(_work, _func, 0)
#define INIT_DELAYED_WORK_ONSTACK(_work, _func) \
__INIT_DELAYED_WORK_ONSTACK(_work, _func, 0)
#define INIT_DEFERRABLE_WORK(_work, _func) \
__INIT_DELAYED_WORK(_work, _func, TIMER_DEFERRABLE)
#define INIT_DEFERRABLE_WORK_ONSTACK(_work, _func) \
__INIT_DELAYED_WORK_ONSTACK(_work, _func, TIMER_DEFERRABLE)
#define INIT_RCU_WORK(_work, _func) \
INIT_WORK(&(_work)->work, (_func))
#define INIT_RCU_WORK_ONSTACK(_work, _func) \
INIT_WORK_ONSTACK(&(_work)->work, (_func))
(1) 延迟工作项(Delayed Work)
- 定义:基于
struct delayed_work
的工作项,结合了定时器(struct timer_list
)。 - 特点:
- 允许在指定延迟时间后执行任务(通过
schedule_delayed_work()
)。 - 定时器默认不可延迟,必须尽快执行(如设备驱动中的紧急任务)。
- 允许在指定延迟时间后执行任务(通过
(2) 可延迟工作项(Deferrable Work)
- 定义:通过
TIMER_DEFERRABLE
标志标记的延迟工作项。 - 特点:
- 允许内核在系统空闲时推迟执行(例如在
TASK_UNINTERRUPTIBLE
状态下)。 - 适用于对实时性要求不高的任务(如日志记录、非紧急的后台处理)。
- 可优化电源管理(减少唤醒次数)或提升系统响应速度。
- 允许内核在系统空闲时推迟执行(例如在
#include <linux/workqueue.h>
// 工作函数
void my_delayed_work_func(struct work_struct *work)
{
printk(KERN_INFO "My delayed work function is running.\n");
}
// 定义并静态初始化延迟工作项
struct work_struct work;
INIT_DELAYED_WORK(my_delayed_work, my_delayed_work_func);
DECLARE_WORK
:创建一个工作项,并进行初始化
/**
* DECLARE_WORK 宏用于声明一个 work_struct 结构体,并将其初始化。
* 这个宏简化了工作队列中工作的创建过程,通过使用 __WORK_INITIALIZER
* 宏来初始化 work_struct 结构体,将指定的函数作为其执行内容。
*
* @param n work_struct 结构体的名称。
* @param f 当工作被调度执行时,将调用的函数。
*/
#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)
总结
INIT_***
宏的功能:- 这些宏(如
INIT_WORK、INIT_DELAYED_WORK
等)用于初始化一个已经声明的工作项,而不是创建新的工作项。 - 它们的主要作用是设置工作项的回调函数和其他必要的初始化操作。
- 这些宏(如
- 与
DECLARE_***
宏的区别:DECLARE_***
宏(如DECLARE_WORK
)不仅初始化工作项,还会声明一个新的变量。- 而
INIT_***
宏仅对已有的变量进行初始化。
2.2.2 创建和销毁工作队列
create_workqueue
:创建一个新的工作队列。
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
示例:
struct workqueue_struct *my_wq;
my_wq = create_workqueue("my_workqueue");
if (!my_wq) {
printk(KERN_ERR "Failed to create workqueue.\n");
return -ENOMEM;
}
destroy_workqueue
:销毁一个工作队列。
void destroy_workqueue(struct workqueue_struct *wq);
示例:
destroy_workqueue(my_wq);
2.2.3 将工作项添加到工作队列
queue_work
:将一个工作项添加到指定的工作队列中。
bool queue_work(struct workqueue_struct *wq, struct work_struct *work);
示例:
if (queue_work(my_wq, &my_work)) {
printk(KERN_INFO "Work item queued successfully.\n");
} else {
printk(KERN_ERR "Failed to queue work item.\n");
}
schedule_work
:将一个工作项添加到系统默认的工作队列中。
bool schedule_work(struct work_struct *work);
示例:
if (schedule_work(&my_work)) {
printk(KERN_INFO "Work item scheduled successfully.\n");
} else {
printk(KERN_ERR "Failed to schedule work item.\n");
}
queue_delayed_work
:将一个延迟工作项添加到指定的工作队列中,在指定的延迟时间后执行。
bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay);
示例:
if (queue_delayed_work(my_wq, &my_delayed_work, msecs_to_jiffies(1000))) {
printk(KERN_INFO "Delayed work item queued successfully.\n");
} else {
printk(KERN_ERR "Failed to queue delayed work item.\n");
}
schedule_delayed_work
:将一个延迟工作项添加到系统默认的工作队列中,在指定的延迟时间后执行。
bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay);
2.2.4 取消工作项
cancel_work_sync
:同步取消一个工作项,如果工作项正在执行,则等待其执行完毕。
bool cancel_work_sync(struct work_struct *work);
示例:
if (cancel_work_sync(&my_work)) {
printk(KERN_INFO "Work item cancelled successfully.\n");
} else {
printk(KERN_INFO "Work item was not queued or already completed.\n");
}
cancel_delayed_work_sync
:同步取消一个延迟工作项,如果工作项正在执行,则等待其执行完毕。
bool cancel_delayed_work_sync(struct delayed_work *dwork);
2.3 示例代码
//示例1
#include <linux/init.h>
#include <linux/module.h>
#include <linux/workqueue.h>
// 工作函数
void my_work_func(struct work_struct *work)
{
printk(KERN_INFO "My work function is running.\n");
}
// 定义并静态初始化工作项
static INIT_WORK(my_work, my_work_func);
// 工作队列指针
static struct workqueue_struct *my_wq;
static int __init my_module_init(void)
{
// 创建工作队列
my_wq = create_workqueue("my_workqueue");
if (!my_wq) {
printk(KERN_ERR "Failed to create workqueue.\n");
return -ENOMEM;
}
// 将工作项添加到工作队列
if (queue_work(my_wq, &my_work)) {
printk(KERN_INFO "Work item queued successfully.\n");
} else {
printk(KERN_ERR "Failed to queue work item.\n");
}
return 0;
}
static void __exit my_module_exit(void)
{
// 取消工作项
cancel_work_sync(&my_work);
// 销毁工作队列
destroy_workqueue(my_wq);
printk(KERN_INFO "Module exited.\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple workqueue example");
//示例2
#include <linux/workqueue.h>
#include <linux/module.h>
struct my_data {
struct work_struct work;
int value;
};
static struct my_data my_data_instance;
static void my_work_func(struct work_struct *work) {
struct my_data *data = container_of(work, struct my_data, work);
printk(KERN_INFO "Work function executed with value %d", data->value);
}
static int __init my_init(void) {
// 初始化工作项
INIT_WORK(&my_data_instance.work, my_work_func);
my_data_instance.value = 42;
// 提交到工作队列
schedule_work(&my_data_instance.work);
return 0;
}
static void __exit my_exit(void) {
// 取消并同步等待工作项完成
cancel_work_sync(&my_data_instance.work);
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
将上述代码保存为 .c
文件,使用内核模块编译工具进行编译,加载和卸载模块即可看到工作队列的运行效果。