为什么 WQ_CPU_INTENSIVE 对 unbound 工作队列没有意义
问题
如题,Linux workqueue 文档在描述 WQ_CPU_INSTENSIVE
时提到:
This flag is meaningless for unbound wq.
为什么这样说?本文尝试去解读这个标记。
unbound wq 就是 WQ_UNBOUND
:
WQ_UNBOUND
Work items queued to an unbound wq are served by the special
worker-pools which host workers which are not bound to any
specific CPU. This makes the wq behave as a simple execution
context provider without concurrency management. The unbound
worker-pools try to start execution of work items as soon as
possible.\ Unbound wq sacrifices locality but is useful for
the following cases.* Wide fluctuation in the concurrency level requirement is
\ expected and using bound wq may end up creating large number
\ of mostly unused workers across different CPUs as the issuer
\ hops through different CPUs.* Long running CPU intensive workloads which can be better
\ managed by the system scheduler.
就是 worker 不绑 CPU,有用的场景一是并发级别波动比较大,一时任务多,一时任务少。另一个就是对长时间运行 CPU intensive 的工作。
什么是 WQ_CPU_INTENSIVE
:
WQ_CPU_INTENSIVE
Work items of a CPU intensive wq do not contribute to the
concurrency level. In other words, runnable CPU intensive
work items will not prevent other work items in the same
worker-pool from starting execution. This is useful for bound
work items which are expected to hog CPU cycles so that their
execution is regulated by the system scheduler.
带这个标记的 wq 表示这个 wq 里面都是 CPU intensive work,这些 works都期望独占CPU,加了这个标记后它们的调度由系统完成。
这些 works do not contribute to the concurrency level 怎么理解?来看code, 参考6.x:
代码分析
带有 CPU intensive 的 wq 源头处理只有 process_one_work
:
static void process_one_work(struct worker *worker, struct work_struct *work) |
注释写到:CPU intensive works 的并发是由 scheduler 管而不归 workqueue 管。具体怎么做的了, 在worker_set_flags()
里。
static inline void worker_set_flags(struct worker *worker, unsigned int flags) |
真正有作用的只有两处:一个是 nr_running--
,另一个就是把 WORKER_CPU_INTENSIVE
这个标记加到 worker 里。
nr_running
:
/* |
怎么用的了?
void wq_worker_running(struct task_struct *task) |
void wq_worker_sleeping(struct task_struct *task) |
static inline void worker_clr_flags(struct worker *worker, unsigned int flags) |
static void unbind_workers(int cpu) |
static bool __need_more_worker(struct worker_pool *pool) |
从以上的使用可以看出来 nr_running
就关联着并发管理,如果没有nr_running
,那就不再需要worker thread了。
回到上文,cpu_intensive
时的nr_running--
就是把这个 worker 从并发里面拿走,这也是 WQ_CPU_INTENSIVE
的意义所在,也正如文档描述的那样。
至于 worker 的 flags 有:
/* worker flags */ |
而 WORKER_CPU_INTENSIVE
的 check 全部都是和 WORKER_NOT_RUNNING
有关。
那影响 CPU_INTENSIVE
行为的只有最开始的 worker_set_flags
,rt? 也就是那个 nr_running--
的条件,也就是说如果 worker->flags
包含 WORKER_NOT_RUNNING
里的一种时,WORKER_CPU_INTENSIVE
这个标记就没意义了。
ok, 那带上 WQ_UNBOUND
来看下:
alloc_workqueue
-> alloc_and_link_pwqs
:
static int alloc_and_link_pwqs(struct workqueue_struct *wq) |
apply_workqueue_attrs() -> apply_workqueue_attrs_locked() -> apply_wqattrs_prepare() -> alloc_unbound_pwq() -> get_unbound_pool() |
get_unbound_pool() |
init_worker_pool
会默认 set pool 为 POOL_DISASSOCIATED
:
/* |
POOL_DISASSOCIATED
就是没有并发管理了,这个 worker pool 是 unbound pool,里面所有 worker 都是 unbound worker,可以在任意 cpu 上执行。
create_worker
会创建一个 worker_thread
:
static struct worker *create_worker(struct worker_pool *pool) |
在后面的 worker_attach_to_pool
时会设置 WORKER_UNBOUND
:
static void worker_attach_to_pool(struct worker *worker, |
ok, 当有 work item 进来时, worker 会被唤醒处理 work item:
__queue_work -> insert_work -> wake_up_worker |
此时,worker 的 flags 是带有 WORKER_UNBOUND
的,也就是这个 worker 表示 WORKER_NOT_RUNNING
,那 cpu intensive时 nr_running
如下就不会递减喽。
/* If transitioning into NOT_RUNNING, adjust nr_running. */ |
so, 点个题:alloc_workqueue()
的 flags
被设置为 WQ_UNBOUND
并且同时是 WQ_CPU_INTENSIVE
时,WQ_CPU_INTENSIVE
是没有发挥作用滴。
版权声明:本站所有文章均采用 CC BY-NC-SA 4.0 CN 许可协议。转载请注明原文链接!