转自:https://blog.csdn.net/cc289123557/article/details/52551176linux
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处连接和本声明。
本文连接:https://blog.csdn.net/l289123557/article/details/52551176
workqueue纳入中断子系统是因为和中断处理有密切关系,写博客重要在于整理本身的思绪,写的时候会把一些不懂的细节问题暴露出来,这样会把问题看的更透彻,workqueue的代码在文件kernel/workqueue.c中,大约5K+行,本文基于linux 4.6.3编写,因为能力有限,本文介绍不全面或者理解有误之处还请见谅,欢迎指正一块儿探讨。多线程
文章系列
1.linux工做队列 - workqueue总览
2.linux工做队列 - workqueue_struct建立
3.linux工做队列 - 把work_struct加入工做队列
4.linux工做队列 - work_struct被调用过程并发
1.介绍
1.1工做队列介绍
有许多状况下须要异步执行进程上下文,在linux中工做队列(wq)是处理这种状况最经常使用的机制。在处理中断中,对于中断下半部的处理最经常使用的就是wq。框架
1.2CMWQ
在原有的wq实现中,一个多线程wq(MT wq)有一个per CPU worker线程,一个单线程wq有一个系统范围的worker线程。多线程wq(MT wq)的数量保持和cpu的个数一致,随着近年来cpu core个数的增长,多线程wq(MT wq)的增长已经使得系统在32k PID的控件接近饱和。异步
虽然MT wq消耗了大量资源,可是提供的并发执行水平仍是得不到知足。即便MT有不多的服务可是ST wq和MT wq仍是有限制。每一个wq有本身单独的worker pool,MTwq智能在per CPU执行,而STwq能够在整个系统执行。函数
CMWQ(Concurrency Managed Workqueue)是对原有wq的一个从新实现,主要解决如下问题:
a. API要可以兼容之前的wq实现
b. 使用per-CPU的worker pool要可以被全部的wq所共享,以便提供更好的并发灵活性,以避免浪费资源
c. 对于使用者要隐藏细节.net
2.workqueue设计框架
工做队列的总体设计框架以下图,这里有多个结构体,他们之间的关系下面说明线程
work_struct结构体表明的是一个任务,它指向一个待异步执行的函数,无论驱动仍是子系统何时要执行这个函数,都必须把work加入到一个workqueue。设计
worker结构体表明一个工做者线程(worker thread),它主要一个接一个的执行挂入到队列中的work,若是没有work了,那么工做者线程就挂起,这些工做者线程被worker-pool管理。blog
对于驱动和子系统的开发人员来讲,接触到的只有work,而背后的处理机制是管理worker-pool和处理挂入的work,这就是CMWQ设计的不一样。
worker-pool结构体用来管理worker,对于每一种worker pool都分两种状况:一种是处理普通work,另外一种是处理高优先级的work。
workqueue_struct结构体表明的是工做队列,工做队列分unbound workqueue和bound workqueue。bound workqueue就是绑定到cpu上的,挂入到此队列中的work只会在相对应的cpu上运行。unbound workqueue不绑定到特定的cpu,并且后台线程池的数量也是动态的,具体workqueue关联到哪一个worker pool,这是由workqueue_attrs决定的。
3.workqueue用法
工做队列从字面意义上很好理解,虽然背后的实现机制有点复杂,可是使用的时候仍是简单方便的,对于使用者来讲,咱们只要定义一个work,而后把work加入到workqueue。若是当前没有咱们须要的workqueue,那么咱们须要本身建立一个。
下文各个API的具体说明见include/linux/workqueue.h
3.1workqueue_struct建立与销毁
//建立workqueue_struct
#define alloc_ordered_workqueue(fmt, flags, args...) \
alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args)
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
#define create_freezable_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \
WQ_MEM_RECLAIM, 1, (name))
#define create_singlethread_workqueue(name) \
alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)
//销毁workqueue_struct
extern void destroy_workqueue(struct workqueue_struct *wq);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
由上能够看出workqueue的建立根据flag的不一样会建立不一样种类的workqueue,但最终都会调用到接口alloc_workqueue,具体workqueue建立的过程及代码分析见文章linux工做队列 - workqueue_struct建立。
3.2初始化work_struct
#define INIT_WORK(_work, _func) \
__INIT_WORK((_work), (_func), 0)
#define INIT_WORK_ONSTACK(_work, _func) \
__INIT_WORK((_work), (_func), 1)
#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)123456789101112131415163.3把work_struct加入工做队列extern bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work);extern bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay);extern bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay);123456具体代码分析见linux工做队列 - 把work_struct加入工做队列————————————————版权声明:本文为CSDN博主「鸭蛋西红柿」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处连接及本声明。原文连接:https://blog.csdn.net/cc289123557/article/details/52551176