Workqueueの話
2014/3/28
第109回、カーネル読書会
あなたは誰ですか?
● 吉田 茂
● ミラクル・リナックス
● Linuxサポートエンジニア
● カーネル素人
最初の貢献
commit 0515973ffb16c2852a1bb1df2ca1456556faaaa5
Author: Shigeru Yoshida <shigeru.yoshida@gmail.com>
Date: Sun Nov 17...
最初の貢献
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index a1591ca..718730d 100644
--- a/kernel/sched/core.c
+++ b...
Concurrency Managed Workqueue
● プロセスコンテキストで非同期に処理を実行
● 処理(コールバック関数)をキューに登録
● ワーカスレッドがキューに登録された処理を実
行
従来のWorkqueue
w0
w1
w2
10ms5ms 5ms
10ms5ms
10ms5ms
50ms
スレッド数=1
Documentation/workqueue.txt
CPUが遊ぶ
w0
w1
w2
10ms5ms 5ms
10ms5ms
10ms5ms
50ms
スレッド数=1
Concurrency Managed Workqueue
w0
w1
w2
10ms5ms 5ms
10ms5ms
10ms5ms
25ms
スレッド数>=3
スレッドプール
Thread
Thread
Thread
Thread Thread
CPU
Job Queue Thread Pool
CPUスケジューラ
ジョブスケジューラ
スレッドプールの悩みどころ
● より多くのJobを受け入れる
→スレッドが増えオーバヘッドが増える
● スレッドの数を制限する
→CPUが遊ぶ
● どのくらいのスレッド数にするか?
→動的にがんばる
2段スケジューラ
Thread
Thread
Thread
Thread Thread
CPU
Job Queue Thread Pool
CPUスケジューラ
ジョブスケジューラ
カーネル
ユーザ(ライブラリ)
すべてはカーネルが知っている
Thread
Thread
Thread
Thread Thread
CPU
Job Queue Thread Pool
CPUスケジューラ
ジョブスケジューラ
全部カーネル!!
APIを読む(1/2)
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
};
schedule_work(struct wor...
APIを読む(2/2)
static void insert_work(struct pool_workqueue *pwq,
struct work_struct *work, struct list_head *head,
unsigned...
ワーカスレッドを読む(1/4)
static int worker_thread(void *__worker)
{
struct worker *worker = __worker;
struct worker_pool *pool = wo...
ワーカスレッドを読む(2/4)
recheck:
if (!need_more_worker(pool))
goto sleep;
if (unlikely(!may_start_working(pool)) &&
manage_workers...
ワーカスレッドを読む(3/4)
do {
struct work_struct *work =
list_first_entry(&pool->worklist,
struct work_struct, entry);
if (likely(!...
ワーカスレッドを読む(4/4)
sleep:
if (unlikely(need_to_manage_workers(pool)) &&
manage_workers(worker))
goto recheck;
worker_enter_id...
CPUスケジューラを読む
static void __sched __schedule(void)
{
... // 変数宣言
need_resched:
preempt_disable();
cpu = smp_processor_id();...
CPUスケジューラを読む(1/3)
smp_mb__before_spinlock();
raw_spin_lock_irq(&rq->lock);
switch_count = &prev->nivcsw;
if (prev->state &...
CPUスケジューラを読む(2/3)
pre_schedule(rq, prev);
if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
n...
CPUスケジューラを読む(3/3)
post_schedule(rq);
sched_preempt_enable_no_resched();
if (need_resched())
goto need_resched;
}
まとめ
Thread
Thread
Thread
Thread Thread
CPU
Workqueue Thread Pool
①work(仕事)をリストに追加
②ワーカスレッドがwork
(仕事)をこなす
③ワーカスレッドが
スリープ
④次...
最後に…
『詳解Linuxカーネル 第3版』 2007, v2.6.11
『Linuxカーネル2.6解読室』 2006, v2.6.15
『Linux Kernel Architecture』 2008,
v2.6.24
『Linux Kern...
おしまい
Upcoming SlideShare
Loading in …5
×

Introduction to workqueue

1,592 views

Published on

Published in: Software
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,592
On SlideShare
0
From Embeds
0
Number of Embeds
57
Actions
Shares
0
Downloads
26
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Introduction to workqueue

  1. 1. Workqueueの話 2014/3/28 第109回、カーネル読書会
  2. 2. あなたは誰ですか? ● 吉田 茂 ● ミラクル・リナックス ● Linuxサポートエンジニア ● カーネル素人
  3. 3. 最初の貢献 commit 0515973ffb16c2852a1bb1df2ca1456556faaaa5 Author: Shigeru Yoshida <shigeru.yoshida@gmail.com> Date: Sun Nov 17 12:12:36 2013 +0900 sched: Fix a trivial typo in comments Fix a trivial typo in rq_attach_root(). Signed-off-by: Shigeru Yoshida <shigeru.yoshida@gmail.com> Signed-off-by: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/20131117.121236.1990617639803941055. shigeru.y Signed-off-by: Ingo Molnar <mingo@kernel.org>
  4. 4. 最初の貢献 diff --git a/kernel/sched/core.c b/kernel/sched/core.c index a1591ca..718730d 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4762,7 +4762,7 @@ static void rq_attach_root(struct rq *rq, struct root_doma cpumask_clear_cpu(rq->cpu, old_rd->span); /* - * If we dont want to free the old_rt yet then + * If we dont want to free the old_rd yet then * set old_rd to NULL to skip the freeing later * in this function: */
  5. 5. Concurrency Managed Workqueue ● プロセスコンテキストで非同期に処理を実行 ● 処理(コールバック関数)をキューに登録 ● ワーカスレッドがキューに登録された処理を実 行
  6. 6. 従来のWorkqueue w0 w1 w2 10ms5ms 5ms 10ms5ms 10ms5ms 50ms スレッド数=1 Documentation/workqueue.txt
  7. 7. CPUが遊ぶ w0 w1 w2 10ms5ms 5ms 10ms5ms 10ms5ms 50ms スレッド数=1
  8. 8. Concurrency Managed Workqueue w0 w1 w2 10ms5ms 5ms 10ms5ms 10ms5ms 25ms スレッド数>=3
  9. 9. スレッドプール Thread Thread Thread Thread Thread CPU Job Queue Thread Pool CPUスケジューラ ジョブスケジューラ
  10. 10. スレッドプールの悩みどころ ● より多くのJobを受け入れる →スレッドが増えオーバヘッドが増える ● スレッドの数を制限する →CPUが遊ぶ ● どのくらいのスレッド数にするか? →動的にがんばる
  11. 11. 2段スケジューラ Thread Thread Thread Thread Thread CPU Job Queue Thread Pool CPUスケジューラ ジョブスケジューラ カーネル ユーザ(ライブラリ)
  12. 12. すべてはカーネルが知っている Thread Thread Thread Thread Thread CPU Job Queue Thread Pool CPUスケジューラ ジョブスケジューラ 全部カーネル!!
  13. 13. APIを読む(1/2) struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; }; schedule_work(struct work_struct *work) -> queue_work(system_wq, work) -> queue_work_on(WORK_CPU_UNBOUND, wq, work) -> __queue_work(cpu, wq, work) -> insert_work(pwq, work, worklist, work_flags)
  14. 14. APIを読む(2/2) static void insert_work(struct pool_workqueue *pwq, struct work_struct *work, struct list_head *head, unsigned int extra_flags) { struct worker_pool *pool = pwq->pool; set_work_pwq(work, pwq, extra_flags); list_add_tail(&work->entry, head); // キューにつなぐ get_pwq(pwq); smp_mb(); if (__need_more_worker(pool)) wake_up_worker(pool); // ワーカスレッドを起こす }
  15. 15. ワーカスレッドを読む(1/4) static int worker_thread(void *__worker) { struct worker *worker = __worker; struct worker_pool *pool = worker->pool; // ワーカスレッドには特別なフラグを立てる worker->task->flags |= PF_WQ_WORKER; woke_up: spin_lock_irq(&pool->lock); if (unlikely(worker->flags & WORKER_DIE)) { spin_unlock_irq(&pool->lock); worker->task->flags &= ~PF_WQ_WORKER; return 0; } worker_leave_idle(worker); // idleスレッドの数を数える
  16. 16. ワーカスレッドを読む(2/4) recheck: if (!need_more_worker(pool)) goto sleep; if (unlikely(!may_start_working(pool)) && manage_workers(worker)) goto recheck; worker_clr_flags(worker, WORKER_PREP | WORKER_REBOUND);
  17. 17. ワーカスレッドを読む(3/4) do { struct work_struct *work = list_first_entry(&pool->worklist, struct work_struct, entry); if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) { // ????? /* optimization path, not strictly necessary */ process_one_work(worker, work); if (unlikely(!list_empty(&worker->scheduled))) process_scheduled_works(worker); } else { move_linked_works(work, &worker->scheduled, NULL); process_scheduled_works(worker); } } while (keep_working(pool)); worker_set_flags(worker, WORKER_PREP, false);
  18. 18. ワーカスレッドを読む(4/4) sleep: if (unlikely(need_to_manage_workers(pool)) && manage_workers(worker)) goto recheck; worker_enter_idle(worker); // idleスレッドの数を数える // スリープ __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irq(&pool->lock); schedule(); goto woke_up; // 起床したら関数のはじめへ }
  19. 19. CPUスケジューラを読む static void __sched __schedule(void) { ... // 変数宣言 need_resched: preempt_disable(); cpu = smp_processor_id(); rq = cpu_rq(cpu); rcu_note_context_switch(cpu); prev = rq->curr; // 現在のプロセスを選択 if (sched_feat(HRTICK)) hrtick_clear(rq);
  20. 20. CPUスケジューラを読む(1/3) smp_mb__before_spinlock(); raw_spin_lock_irq(&rq->lock); switch_count = &prev->nivcsw; if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { if (unlikely(signal_pending_state(prev->state, prev))) { prev->state = TASK_RUNNING; } else { deactivate_task(rq, prev, DEQUEUE_SLEEP); prev->on_rq = 0; if (prev->flags & PF_WQ_WORKER) { struct task_struct *to_wakeup; to_wakeup = wq_worker_sleeping(prev, cpu); if (to_wakeup) try_to_wake_up_local(to_wakeup); } } switch_count = &prev->nvcsw; }
  21. 21. CPUスケジューラを読む(2/3) pre_schedule(rq, prev); if (unlikely(!rq->nr_running)) idle_balance(cpu, rq); put_prev_task(rq, prev); next = pick_next_task(rq); // 次のプロセスを選択 clear_tsk_need_resched(prev); rq->skip_clock_update = 0; if (likely(prev != next)) { rq->nr_switches++; rq->curr = next; ++*switch_count; context_switch(rq, prev, next); // コンテキストスイッチ cpu = smp_processor_id(); rq = cpu_rq(cpu); } else raw_spin_unlock_irq(&rq->lock);
  22. 22. CPUスケジューラを読む(3/3) post_schedule(rq); sched_preempt_enable_no_resched(); if (need_resched()) goto need_resched; }
  23. 23. まとめ Thread Thread Thread Thread Thread CPU Workqueue Thread Pool ①work(仕事)をリストに追加 ②ワーカスレッドがwork (仕事)をこなす ③ワーカスレッドが スリープ ④次のワーカスレッドにバ トンタッチ!!
  24. 24. 最後に… 『詳解Linuxカーネル 第3版』 2007, v2.6.11 『Linuxカーネル2.6解読室』 2006, v2.6.15 『Linux Kernel Architecture』 2008, v2.6.24 『Linux Kernel Development』 2010, v2.6.34
  25. 25. おしまい

×