kernel之工作队列workqueue
背景
之前在触摸驱动(比如汇顶等)的代码里,会看到INIT_WORK
等相关字眼,只知道是和工作队列相关,没有深入研究学习。
最近在看蓝牙HCI相关代码中,又看到了INIT_WORK
等,觉得工作队列(workqueue)需要好好看看,并记录下
工作队列通常用于将耗时工作滞后处理,比如中断处理的下半部耗时操作,等, 中断相关的处理机制可见:《Kernel之中断处理底半部机制》。
最新的 workqueue 实现叫做 CMWQ(Concurrency Managed Workqueue),也就是用更加智能的算法来实现“并行和节省”。
Kernel: v5.4.18
几个概念
很容易混淆的几个概念:
- work :工作。
- workqueue :工作的集合。workqueue 和 work 是一对多的关系。
- worker :工人。在代码中 worker 对应一个 work_thread() 内核线程。
- worker_pool:工人的集合。worker_pool 和 worker 是一对多的关系。
- pwq(pool_workqueue):中间人 / 中介,负责建立起 workqueue 和 worker_pool 之间的关系。workqueue 和 pwq 是一对多的关系,pwq 和 worker_pool 是一对一的关系。
相关关系图:
当前work内核线程
查看系统当前work线程$ ps -ef | grep work
root 600 2 0 10:36 ? 00:00:00 [kworker/0:1H-kblockd]
root 741 2 0 10:36 ? 00:00:00 [kworker/9:1H-kblockd]
root 766 2 0 10:36 ? 00:00:00 [kworker/u25:2-rb_allocator]
root 785 2 0 10:36 ? 00:00:00 [kworker/6:1H-kblockd]
root 792 2 0 10:36 ? 00:00:00 [kworker/11:4-cgroup_destroy]
root 837 2 0 10:36 ? 00:00:00 [kworker/4:1H-kblockd]
root 838 2 0 10:36 ? 00:00:00 [kworker/u25:3-rb_allocator]
worker线程被命名成了”kworker/n:x”的格式,其中n是worker线程所在的CPU的编号,x是其在worker pool中的编号,如果带了”H”后缀,说明这是高优先级的worker pool
还有一些带”u”前缀的,它表示”unbound”,意思是这个worker线程不和任何的CPU绑定,而是被所有CPU共享,这种设计主要是为了增加灵活性。”u”后面的这个数字也不再表示CPU的编号,而是表示由这些unbound的worker线程组成的worker pool的ID号
主要相关函数
主要源代码: /kernel/workqueue.c
INIT_WORK
系列, 工作初始化
定义了work和work对应的操作_func,将其绑定:
struct work_struct my_work; /* 定义一个work */
void my_work_func(struct work_struct *work) /* 定义一个work处理函数 */
/* 将work和work处理函数绑定 */
INIT_WORK(&my_work, my_work_func);
/* 延时work */
INIT_DELAYED_WORK(&my_work, my_work_func);
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
work调度系列
/* 调度work执行 */
//对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。
//工作者线程本质上是一个普通的内核线程,在默认情况下,每个CPU均有一个类型为“events”的工作者线程,当调用schedule_work时,这个工作者线程会被唤醒去执行工作链表上的所有工作。
static inline bool schedule_work(struct work_struct *work)
{
return queue_work(system_wq, work); //调度在缺省系统队列
}
//延迟执行工作,调度在缺省系统队列
static inline bool schedule_delayed_work(struct delayed_work *dwork,
unsigned long delay)
{
return queue_delayed_work(system_wq, dwork, delay);//延时调度在缺省系统队列
}
//延迟执行工作在指定的cpu,调度在缺省系统队列
static inline bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork,
unsigned long delay)
{
return queue_delayed_work_on(cpu, system_wq, dwork, delay);
}
//类似于schedule_work,区别在于queue_work把给定工作提交给创建的工作队列wq而不是缺省队列。
static inline bool queue_work(struct workqueue_struct *wq,
struct work_struct *work)
{
return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}
//延迟执行工作, 调度在指定的工作队列, queue work on a workqueue after delay
static inline bool queue_delayed_work(struct workqueue_struct *wq,
struct delayed_work *dwork,
unsigned long delay)
{
return queue_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay);
}
//延迟执行工作在指定的cpu,调度在指定的工作队列
bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay);
自定义工作队列
//自定义工作队列,创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。
//旧版本的创建函数 create_workqueue() 已经被新版本的 alloc_workqueue() 取代
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
#define create_singlethread_workqueue(name) \
alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)
//allocate an ordered workqueue
#define alloc_ordered_workqueue(fmt, flags, args...) \
alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | \
__WQ_ORDERED_EXPLICIT | (flags), 1, ##args)
//allocate a workqueue
struct workqueue_struct *alloc_workqueue(const char *fmt,
unsigned int flags,
int max_active, ...);
刷新工作队列
//刷新缺省工作队列。此函数会一直等待,直到队列中的所有工作都被执行。
static inline void flush_scheduled_work(void)
{
flush_workqueue(system_wq); //刷新缺省的系统工作队列
}
//刷新指定工作队列。
void flush_workqueue(struct workqueue_struct *wq);
取消及注销工作和队列
//取消延迟工作
bool cancel_delayed_work(struct delayed_work *dwork);
//cancel a delayed work and wait for it to finish
bool cancel_delayed_work_sync(struct delayed_work *dwork);
//释放创建的工作队列。
void destroy_workqueue(struct workqueue_struct *wq);
//终止队列中的任务或者阻塞任务直到回调结束(如果处理程序已经在处理该任务)
bool cancel_work_sync(struct work_struct *work);;
参考
原理及实现机制:
https://zhuanlan.zhihu.com/p/94561631
http://kernel.meizu.com/linux-workqueue.html
http://www.wowotech.net/irq_subsystem/workqueue.html