Introduction to workqueue
description
Transcript of Introduction to workqueue
Workqueueの話2014/3/28第109回、カーネル読書会
あなたは誰ですか?
● 吉田 茂● ミラクル・リナックス● Linuxサポートエンジニア● カーネル素人
最初の貢献
commit 0515973ffb16c2852a1bb1df2ca1456556faaaa5Author: Shigeru Yoshida <[email protected]>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 <[email protected]> Signed-off-by: Peter Zijlstra <[email protected]> Link: http://lkml.kernel.org/r/20131117.121236.1990617639803941055.shigeru.y Signed-off-by: Ingo Molnar <[email protected]>
最初の貢献
diff --git a/kernel/sched/core.c b/kernel/sched/core.cindex 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: */
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
スレッドプール
ThreadThread
Thread
Thread Thread
CPU
Job Queue Thread Pool
CPUスケジューラ
ジョブスケジューラ
スレッドプールの悩みどころ
● より多くのJobを受け入れる
→スレッドが増えオーバヘッドが増える
● スレッドの数を制限する
→CPUが遊ぶ
● どのくらいのスレッド数にするか?
→動的にがんばる
2段スケジューラ
ThreadThread
Thread
Thread Thread
CPU
Job Queue Thread Pool
CPUスケジューラ
ジョブスケジューラ
カーネル
ユーザ(ライブラリ)
すべてはカーネルが知っている
ThreadThread
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 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)
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); // ワーカスレッドを起こす
}
ワーカスレッドを読む(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スレッドの数を数える
ワーカスレッドを読む(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);
ワーカスレッドを読む(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);
ワーカスレッドを読む(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; // 起床したら関数のはじめへ
}
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);
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;
}
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);
} elseraw_spin_unlock_irq(&rq->lock);
CPUスケジューラを読む(3/3)
post_schedule(rq);sched_preempt_enable_no_resched();if (need_resched())
goto need_resched;}
まとめ
ThreadThread
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 Kernel Development』 2010, v2.6.34
おしまい