1 概述
Workqueue
工作队列是利用内核线程来异步执行工作任务的通用机制;Workqueue
工作队列可以用作中断处理的Bottom-half
机制,利用进程上下文来执行中断处理中耗时的任务,因此它允许睡眠,而Softirq
和Tasklet
在处理任务时不能睡眠;
来一张概述图:
- 在中断处理过程中,或者其他子系统中,调用
workqueue
的调度或入队接口后,通过建立好的链接关系图逐级找到合适的worker
,最终完成工作任务的执行;
2 数据结构
2.1 总览
先看看关键的数据结构:
work_struct
:工作队列调度的最小单位,work item
;workqueue_struct
:工作队列,work item
都挂入到工作队列中;worker
:work item
的处理者,每个worker
对应一个内核线程;worker_pool
:worker
池(内核线程池),是一个共享资源池,提供不同的worker
来对work item
进行处理;pool_workqueue
:充当桥梁纽带的作用,用于连接workqueue
和worker_pool
,建立链接关系;
【文章福利】小编推荐自己的Linux内核技术交流群: 【977878001】整理一些个人觉得比较好得学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100进群领取,额外赠送一份 价值699的内核资料包(含视频教程、电子书、实战项目及代码)
内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料
学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议
2.2 work
struct work_struct
用来描述work
,初始化一个work
并添加到工作队列后,将会将其传递到合适的内核线程来进行处理,它是用于调度的最小单位。
关键字段描述如下:
struct work_struct {
atomic_long_t data; //低比特存放状态位,高比特存放worker_pool的ID或者pool_workqueue的指针
struct list_head entry; //用于添加到其他队列上
work_func_t func; //工作任务的处理函数,在内核线程中回调
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
图片说明下data
字段:
2.3 workqueue
内核中工作队列分为两种:
- bound:绑定处理器的工作队列,每个
worker
创建的内核线程绑定到特定的CPU上运行; - unbound:不绑定处理器的工作队列,创建的时候需要指定
WQ_UNBOUND
标志,内核线程可以在处理器间迁移;
内核默认创建了一些工作队列(用户也可以创建):
system_mq
:如果work item
执行时间较短,使用本队列,调用schedule[_delayed]_work[_on]()
接口就是添加到本队列中;system_highpri_mq
:高优先级工作队列,以nice值 -20 来运行;system_long_wq
:如果work item
执行时间较长,使用本队列;system_unbound_wq
:该工作队列的内核线程不绑定到特定的处理器上;system_freezable_wq
:该工作队列用于在Suspend时可冻结的work item
;system_power_efficient_wq
:该工作队列用于节能目的而选择牺牲性能的work item
;system_freezable_power_efficient_wq
:该工作队列用于节能或Suspend时可冻结目的的work item
;
struct workqueue_struct
关键字段介绍如下:
struct workqueue_struct {
struct list_head pwqs; /* WR: all pwqs of this wq */ //所有的pool_workqueue都添加到本链表中
struct list_head list; /* PR: list of all workqueues */ //用于将工作队列添加到全局链表workqueues中
struct list_head maydays; /* MD: pwqs requesting rescu