工作队列 ( workqueue )
2015-07-25 17:21
543 查看
1. 有些时候内核需要一个异步的进程执行上下文,而工作
队列(workqueue)可以满足这种需求。
工作队列中的每一个元素都是一个工作项(work item),
有一个函数与工作项相关,这个函数就是工作项所要处
理的任务。
内核中有一个专门的线程——被称作worker,来依次执行
工作队列中的每一个工作项对应的函数,当工作队列为
空时,这个worker就变为空闲状态(idle),当有新的
工作项加入到工作队列时,worker又重新开始执行。
2. 在最早的实现中包括两种实现方式,一种是整个系统只
有一个worker(single thread,ST),另一种是每个
CPU包含一个worker(multiple thread,MT),每一个
CPU包含一个属于自己的worker pool 。
这两种实现都引起了系统中对于这种异步上下文的竞争,
只不过是MT方式的竞争可能更小一些。
因此,内核人员对workqueue作了重新实现,新的实现
被称作concurrency managed workqueue(cmwq),新的
实现的特点如下:
* 与之前的API兼容
* 实现了统一的每CPU worker pool,减少了资源的浪
费,提高了并发的灵活性
* 可以自动调节worker pool和并发的级别
3. 为了简化执行这种异步上下文 ,引入了work item,它
是一个简单的结构体,包含一个函数指针,这个指针指
向的函数就是需要在异步上文中执行的函数。
当一个内核子系统或者驱动程序需要在异步上下文中执
行一个函数时,它首先需要创建一个work item结构体,
然后将这个结构体加入到工作队列中。
工作者线程(worker thread)负责从工作对列中取出work
item并执行,直到工作队列中的work item为空。工作
者线程是由worker-pool管理的。
cmwq的实现在用户接口(即子系统或者驱动程序的使用)
和后台支持上(即如何管理worker pool以及处理work
item)有差别。
每一个CPU上都有两个worker pool,一个是用来处理普
通的work item,另一个是用来处理高优先级的work item。
另外还有一些worker pool用来处理未添加到绑定到CPU
的wq上的work item,这些worker pool的数量是动态的。
可以通过修改工作队列的属性来改变添加到其上的work
item的执行行为,例如在哪个CPU上执行、并发限制、优
先级等。
当一个work item添加到workqueue时,根据函数的参数
以及要添加到的工作队列的属性就可以确定将由哪一个
worker pool来执行,并且会将该work item添加到该
worker pool共享的工作列表中。例如,当一个work
item被添加到一个workqueue时,它要么被添加到普通
的worker-pool的工作列表,要么被添加到高优先级的
worker-pool的工作列表,这里的两个worker-pool对应
于添加work item到工作队列的CPU。
管理一个工作者线程池的并发度一直是工作者线程池面
临的一个重要的问题。cmwq保持了最小的并发度,但是
又让CPU不会空闲,充分利用了CPU资源。
每一个绑定到CPU的线程池通过在调度器中添加钩子实
现了并发的控制。当一个工作项被唤醒或者睡眠的时候,
工作者线程池都会接到通知,以此来追踪当前并发的工
作者线程数。一般来说,当一个CPU上有一个或者多个
工作者线程在执行的时候,与该CPU绑定的工作者线程
池不会再起动其他的工作者线程,当该CPU上最后一个
工作者线程睡眠后,立刻启动一个新的工作,来保证CPU
不会空转。
对于未绑定到CPU的工作队列来说,它的线程池数量是
动态的。可以通过apply_workqueue_attrs函数来设置这
个未绑定的工作队列的参数,系统会自动生成与这些参
数对应的工作者线程池。另外对于绑定的工作队列可以
设置某些参数,让某个工作队列忽略并发的限制。
任何需要多个工作者线程同时执行的子系统或者驱动程
序都需要使用有急救工作者线程(rescuer worker)的
工作队列。例如,在内存回收的时候,回收内存的工作
项往往需要同时执行,因此如果没有使用这种工作队列
的话,就会造成死锁,因为后执行的工作者线程会等待
前面的工作者线程被释放。
4. 应用程序接口(API)
alloc_workqueue负责创建一个工作队列,之前的create_*workqueue
之类的接口已经被丢弃了。该函数有三个参数@name,
@flags以及@max_active,@name表示该工作队列的名字,
如果急救工作者线程也存在的话,那么@name也是该急救
工作者线程的名字。
工作队列已经不再管理执行资源,但是它作为一个域用
来管理工作项,例如保证工作项往前执行、flush以及
工作项的属性。@flags和@max_active参数控制工作项
的执行资源分配,如何被调度以及如何执行。
@flags:
WQ_UNBOUND
未绑定的工作队列对应的工作者线程池数是动态的。该
工作者线程池未绑定到任何的CPU,因此相当于是一个简
单的执行上下文的提供者,它会立即执行添加到该工作
队列工作项。一般在下面两种情况会用到:
* 工作者线程的并发级别波动很大,且将工作项添加到
工作队列的发起者会在不同的CPU之间来回执行,因
此如果不使用未绑定工作队列的话,就会造成在各个
CPU的工作者线程池上创建了许多不会使用的工作者
线程。
* 当CPU的负担很重时,最好将工作项添加到位绑定的工
作队列,由调度器来进行调度。
WQ_FREEZABLE
当系统暂停时,工作队列会进入到冻结状态。并且会清
空该工作队列中的工作项,直到被解冻才可以执行新的
工作。
WQ_MEM_RECLAIM
任何可能在内存回收路径上使用的工作队列都必须设置
该标记,它保证了不管内存压力有多大,都至少有一个
执行上下文与之对应。
WQ_HIGHPRI
高优先级工作队列的工作项被加入到相应CPU的高优先
级工作者线程池。高优先级工作者线程池由提高了nice
级别的线程来服务。
普通的工作者线程池和高优先级的工作者线程池是隔离
的,他们之间不进行任何的交互。对于并发级别的控制
也是各自独立进行控制的。
WQ_CPU_INTENSIVE
这种工作队列中的工作项并不受并发级别的控制,因为
该类型的工作项通常会需要很多的CPU使用量,因此最
好的办法就是由系统调度器来进行调度。
另外,并发级别的限制会影响密集型工作项的执行,例
如当前正在运行的非密集型的工作会延迟密集型工作的
执行。
该标记对未绑定的工作队列是无效的。
@max_active:
表示每个工作队列同时最多能有几个工作项在同一个CPU
上运行。例如,max_active=16,表示同时最多能有16个
该工作队列的工作项在每一个CPU上运行。
对于绑定的工作队列,@max_active的最大值为512,默
认情况下,该参数传值为0,此时它的最大值为256。对
于未绑定的工作队列,max_active的最大值要大于512。
工作队列的同时处于活跃状态的工作项的数目是由用户
调节的,例如用户同时添加到工作队列中的工作项的数
目。除非有需求调节活跃工作项的数目,否则,推荐该
参数赋值为0。
有些需要依赖ST工作队列的顺序,因此可以使WQ_UNBOUND
和@max_active为1。这样就模拟了ST,每一个该类型的
工作项都应该被添加到未绑定的工作队列中,且一次只
能有一个工作项执行就限定了执行的顺序。
5. 指导方针
* 如果工作队列中的工作项可能用在内存回收代码路径
中,一定要设置WQ_MEM_RECLAIM,每一个该类型的队
列都有一个保留的执行上下文。另外,如果在内存回
收路径中有多个工作项相互依赖的话,应该将这些工
作项添加到不同的工作对列中。
* 如果没有特殊要求的话,推荐@max_active参数的值
为0。
* 一般如果没有执行次序要求的话,不会用到ST。
* 工作队列作为工作项一个域,用来WQ_MEM_RECLAIM,
flush以及某些工作项的共有属性。如果工作项不会
用到上面的任何一个特性,可以使用系统工作队列。
* 除非一个工作项要耗费很多的CPU,一般将工作项添
加到绑定的工作队列中。
ref
===
1. Documentation/workqueue.txt
队列(workqueue)可以满足这种需求。
工作队列中的每一个元素都是一个工作项(work item),
有一个函数与工作项相关,这个函数就是工作项所要处
理的任务。
内核中有一个专门的线程——被称作worker,来依次执行
工作队列中的每一个工作项对应的函数,当工作队列为
空时,这个worker就变为空闲状态(idle),当有新的
工作项加入到工作队列时,worker又重新开始执行。
2. 在最早的实现中包括两种实现方式,一种是整个系统只
有一个worker(single thread,ST),另一种是每个
CPU包含一个worker(multiple thread,MT),每一个
CPU包含一个属于自己的worker pool 。
这两种实现都引起了系统中对于这种异步上下文的竞争,
只不过是MT方式的竞争可能更小一些。
因此,内核人员对workqueue作了重新实现,新的实现
被称作concurrency managed workqueue(cmwq),新的
实现的特点如下:
* 与之前的API兼容
* 实现了统一的每CPU worker pool,减少了资源的浪
费,提高了并发的灵活性
* 可以自动调节worker pool和并发的级别
3. 为了简化执行这种异步上下文 ,引入了work item,它
是一个简单的结构体,包含一个函数指针,这个指针指
向的函数就是需要在异步上文中执行的函数。
当一个内核子系统或者驱动程序需要在异步上下文中执
行一个函数时,它首先需要创建一个work item结构体,
然后将这个结构体加入到工作队列中。
工作者线程(worker thread)负责从工作对列中取出work
item并执行,直到工作队列中的work item为空。工作
者线程是由worker-pool管理的。
cmwq的实现在用户接口(即子系统或者驱动程序的使用)
和后台支持上(即如何管理worker pool以及处理work
item)有差别。
每一个CPU上都有两个worker pool,一个是用来处理普
通的work item,另一个是用来处理高优先级的work item。
另外还有一些worker pool用来处理未添加到绑定到CPU
的wq上的work item,这些worker pool的数量是动态的。
可以通过修改工作队列的属性来改变添加到其上的work
item的执行行为,例如在哪个CPU上执行、并发限制、优
先级等。
当一个work item添加到workqueue时,根据函数的参数
以及要添加到的工作队列的属性就可以确定将由哪一个
worker pool来执行,并且会将该work item添加到该
worker pool共享的工作列表中。例如,当一个work
item被添加到一个workqueue时,它要么被添加到普通
的worker-pool的工作列表,要么被添加到高优先级的
worker-pool的工作列表,这里的两个worker-pool对应
于添加work item到工作队列的CPU。
管理一个工作者线程池的并发度一直是工作者线程池面
临的一个重要的问题。cmwq保持了最小的并发度,但是
又让CPU不会空闲,充分利用了CPU资源。
每一个绑定到CPU的线程池通过在调度器中添加钩子实
现了并发的控制。当一个工作项被唤醒或者睡眠的时候,
工作者线程池都会接到通知,以此来追踪当前并发的工
作者线程数。一般来说,当一个CPU上有一个或者多个
工作者线程在执行的时候,与该CPU绑定的工作者线程
池不会再起动其他的工作者线程,当该CPU上最后一个
工作者线程睡眠后,立刻启动一个新的工作,来保证CPU
不会空转。
对于未绑定到CPU的工作队列来说,它的线程池数量是
动态的。可以通过apply_workqueue_attrs函数来设置这
个未绑定的工作队列的参数,系统会自动生成与这些参
数对应的工作者线程池。另外对于绑定的工作队列可以
设置某些参数,让某个工作队列忽略并发的限制。
任何需要多个工作者线程同时执行的子系统或者驱动程
序都需要使用有急救工作者线程(rescuer worker)的
工作队列。例如,在内存回收的时候,回收内存的工作
项往往需要同时执行,因此如果没有使用这种工作队列
的话,就会造成死锁,因为后执行的工作者线程会等待
前面的工作者线程被释放。
4. 应用程序接口(API)
alloc_workqueue负责创建一个工作队列,之前的create_*workqueue
之类的接口已经被丢弃了。该函数有三个参数@name,
@flags以及@max_active,@name表示该工作队列的名字,
如果急救工作者线程也存在的话,那么@name也是该急救
工作者线程的名字。
工作队列已经不再管理执行资源,但是它作为一个域用
来管理工作项,例如保证工作项往前执行、flush以及
工作项的属性。@flags和@max_active参数控制工作项
的执行资源分配,如何被调度以及如何执行。
@flags:
WQ_UNBOUND
未绑定的工作队列对应的工作者线程池数是动态的。该
工作者线程池未绑定到任何的CPU,因此相当于是一个简
单的执行上下文的提供者,它会立即执行添加到该工作
队列工作项。一般在下面两种情况会用到:
* 工作者线程的并发级别波动很大,且将工作项添加到
工作队列的发起者会在不同的CPU之间来回执行,因
此如果不使用未绑定工作队列的话,就会造成在各个
CPU的工作者线程池上创建了许多不会使用的工作者
线程。
* 当CPU的负担很重时,最好将工作项添加到位绑定的工
作队列,由调度器来进行调度。
WQ_FREEZABLE
当系统暂停时,工作队列会进入到冻结状态。并且会清
空该工作队列中的工作项,直到被解冻才可以执行新的
工作。
WQ_MEM_RECLAIM
任何可能在内存回收路径上使用的工作队列都必须设置
该标记,它保证了不管内存压力有多大,都至少有一个
执行上下文与之对应。
WQ_HIGHPRI
高优先级工作队列的工作项被加入到相应CPU的高优先
级工作者线程池。高优先级工作者线程池由提高了nice
级别的线程来服务。
普通的工作者线程池和高优先级的工作者线程池是隔离
的,他们之间不进行任何的交互。对于并发级别的控制
也是各自独立进行控制的。
WQ_CPU_INTENSIVE
这种工作队列中的工作项并不受并发级别的控制,因为
该类型的工作项通常会需要很多的CPU使用量,因此最
好的办法就是由系统调度器来进行调度。
另外,并发级别的限制会影响密集型工作项的执行,例
如当前正在运行的非密集型的工作会延迟密集型工作的
执行。
该标记对未绑定的工作队列是无效的。
@max_active:
表示每个工作队列同时最多能有几个工作项在同一个CPU
上运行。例如,max_active=16,表示同时最多能有16个
该工作队列的工作项在每一个CPU上运行。
对于绑定的工作队列,@max_active的最大值为512,默
认情况下,该参数传值为0,此时它的最大值为256。对
于未绑定的工作队列,max_active的最大值要大于512。
工作队列的同时处于活跃状态的工作项的数目是由用户
调节的,例如用户同时添加到工作队列中的工作项的数
目。除非有需求调节活跃工作项的数目,否则,推荐该
参数赋值为0。
有些需要依赖ST工作队列的顺序,因此可以使WQ_UNBOUND
和@max_active为1。这样就模拟了ST,每一个该类型的
工作项都应该被添加到未绑定的工作队列中,且一次只
能有一个工作项执行就限定了执行的顺序。
5. 指导方针
* 如果工作队列中的工作项可能用在内存回收代码路径
中,一定要设置WQ_MEM_RECLAIM,每一个该类型的队
列都有一个保留的执行上下文。另外,如果在内存回
收路径中有多个工作项相互依赖的话,应该将这些工
作项添加到不同的工作对列中。
* 如果没有特殊要求的话,推荐@max_active参数的值
为0。
* 一般如果没有执行次序要求的话,不会用到ST。
* 工作队列作为工作项一个域,用来WQ_MEM_RECLAIM,
flush以及某些工作项的共有属性。如果工作项不会
用到上面的任何一个特性,可以使用系统工作队列。
* 除非一个工作项要耗费很多的CPU,一般将工作项添
加到绑定的工作队列中。
ref
===
1. Documentation/workqueue.txt
相关文章推荐
- UIButton的titleEdgeInsets和imageEdgeInsets使用技巧
- MIUI应用权限设置
- 【从零开始学NGUI 】 (九)分页效果实现
- 数组作为实参和形参的形式是怎样的?zhidao.baidu.com/link?url=owojlL0OUiAYU50L9g86kmo5AToWjv42ZQo9WN0HhtvHzGbcU7etRbJ69
- HDU 5301 Buildings 建公寓(逻辑,水)
- hdu5297 Y sequence 容斥加迭代
- UiAutomator——环境搭建及简单使用
- 通过回调实现Service中更新UI。
- CF Gym 100637K Microcircuits (DP)
- NGUI 3.5课程(五岁以下儿童)button-图片切换
- POJ 1679 The Unique MST
- ios学习整理(二)使用drawRect:方法自定义绘图和UIBezierPath类
- iOS开发-UITableView单选多选/复选实现1
- Ubuntu GUI工具及其对应命令
- Android—开发自学历程(1)-Activity(4)-UI布局
- hdu3530 Subsequence 单调队列
- select sequence.currval 时报错 ORA-08002: 序列XXXX尚未在此进程中定义
- UItableview x详解
- iOS7中UIBarButtonItem、UINavigationItem、UINavigationBar、UIToolbar之间的关系
- EasyUI Tabs绑定右键