Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

Linuxのプロセススケジューラ(Reading the Linux process scheduler)

10.186 visualizaciones

Publicado el

NLKB-005

Publicado en: Software
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Responder 
    ¿Estás seguro?    No
    Tu mensaje aparecerá aquí

Linuxのプロセススケジューラ(Reading the Linux process scheduler)

  1. 1. Linuxのプロセススケジューラ (Reading the Linux process scheduler) Copyright Hitachi Ltd. 2014. All rights reserved. 日立製作所 横浜研究所 豊岡 拓 (hiraku.toyooka.gu@hitachi.com) ! Linux 3.15.0版
  2. 2. プロセススケジューラに関す るトピックの全体像 プロセススケジューラ スケジューリング・クラス CFS Real-time Copyright Hitachi Ltd. 2014. All rights reserved. Dead-line ロードバランスグループ・ スケジューリング カーネル内 プリエンプション stop idle 省電力 割り込み, スピンロック プロセス/スレッド, 時間管理, 高精度タイマ, tick管理, etc.. wait-queue, セマフォ, ミューテックス, 依存 ユーザ空間へのI/F(システムコール等) etc.. 2 共通部(優先度、データ構造、関数)
  3. 3. プロセススケジューリングとは • 複数のプロセスに対して、どれだけのCPU時間を、どのよう な順序で割り当てるかを決める方法 • 目標(全部を同時には満たせない) • 高スループット • 低レイテンシ • 公平性の実現 • 実時間制約を満たす • 省電力 Copyright Hitachi Ltd. 2014. All rights reserved. 3
  4. 4. スケジューリングクラスとポリシー • Fairクラス • SCHED_OTHER, SCHED_BATCH, SCHED_IDLE • Real-timeクラス • SCHED_FIFO, SCHED_RR • Deadlineクラス • SCHED_DEADLINE • (Stopクラス、Idleクラス) • ユーザは使用できない(stop_machineやidleスレッドの実装) Copyright Hitachi Ltd. 2014. All rights reserved. 4
  5. 5. Fairクラス(CFS) • プロセス間の公平性を保ちつつ、CPU利用効率の最大化とイベント処理へ のレスポンス高速化を両立 • 累積実行時間が最も少ないプロセスにCPUを割り当てる • ただし、実行可能プロセス数が多い時にプロセス切り替えが頻繁に起こ るのを防ぐため、最小のタイムスライスを持つ • SCHED_OTHER: デフォルトのポリシー • SCHED_BATCH: CPU-intensiveだと仮定してスイッチを起こしにくくする • SCHED_IDLE: 他に動けるプロセスがいない時だけ実行される Copyright Hitachi Ltd. 2014. All rights reserved. 5
  6. 6. Real-timeクラス • 静的に与えられた優先度の高い順にプロセスをスケジュー ル • SCHED_FIFO • 自発的にCPUを手放さない限り実行し続ける • SCHED_RR • 最高優先度のプロセスが複数ある場合、与えられた タイムスライスによってラウンドロビン実行する Copyright Hitachi Ltd. 2014. All rights reserved. 6
  7. 7. Deadlineクラス • SCHED_DEADLINE • 処理周期、期限、最大実行時間をプロセスに 与え、期限の早いプロセスから順番にスケ ジュール Copyright Hitachi Ltd. 2014. All rights reserved. 7
  8. 8. Copyright Hitachi Ltd. 2014. All rights reserved. 優先度 8 ポリシー優先度 (カーネル内) 優先度 優先度 (ユーザ空間) nice値(ps) SCHED_DEADLINE -1 - - -101 SCHED_FIFO, SCHED_RR 0 99 -100 1 98 -99 . . . . . . 97 2 -3 98 1 -2 SCHED_OTHER, SCHED_BATCH 100 0 -20 0 101 -19 1 . . . . . . 120(default) 0 20 . . . . . . 138 18 38 139 19 39 SCHED_IDLE - - - 高い 低い
  9. 9. データ構造 • プロセス構造体 (struct task_struct) • プロセスの状態フラグ • スケジューリング・クラス (struct sched_class) • ランキュー (struct rq) Copyright Hitachi Ltd. 2014. All rights reserved. 9
  10. 10. struct task_struct • プロセスを表す構造体 • クラス固有のスケジューリング情報は各クラスの 「エンティティ」で表される 型名前説明 long state プロセスの状態フラグ int on_rq ランキュー上にプロセスが存在するか否か int prio プロセスの優先度 unsigned int policy プロセスのスケジューリングポリシー struct sched_class *sched_class プロセスのスケジューリングクラス struct sched_entity se fairクラス用のスケジューリングエンティティ struct sched_rt_entity rt rtクラス用のスケジューリングエンティティ struct sched_dl_entity dl deadlineクラス用のスケジューリングエンティティ Copyright Hitachi Ltd. 2014. All rights reserved. 10
  11. 11. プロセスの状態フラグ フラグ説明 TASK_RUNNING 実行可能状態(実行中を含む) TASK_INTERRUPTIBLE 条件待ちによる停止中 TASK_UNINTERRUPTIBLE 同上。シグナルによる割り込み不可 EXIT_ZOMBIE 実行終了後、プロセス構造体の回収待ち EXIT_DEAD プロセス構造体の回収開始後の状態 TASK_DEAD exit終了後の最後のプロセススイッチ時にスケ Copyright Hitachi Ltd. 2014. All rights reserved. 11 ジューリング・クラス固有の処理を呼び出す TASK_STOPPED シグナル受信による停止中 TASK_TRACED デバッガにより停止中(ptraceで監視中にシグ ナル受信) TASK_WAKING 起床中
  12. 12. プロセスの一生 (none) fork/clone Copyright Hitachi Ltd. 2014. All rights reserved. 12 RUNNING INTERRUPTIBLE or UNINTERRUPTIBLE or STOPPED or TRACED EXIT_ZOMBIE EXIT_DEAD exit wait WAKING
  13. 13. struct sched_class • スケジューリングクラスを表す構造体 • クラス固有処理の関数ポインタの表 • スケジューラ共通処理からクラス固有処理への 呼び出しインタフェース Copyright Hitachi Ltd. 2014. All rights reserved. 13
  14. 14. struct sched_class Copyright Hitachi Ltd. 2014. All rights reserved. 14 struct sched_class { const struct sched_class *next; ! void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags); void (*yield_task) (struct rq *rq); bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt); void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags); struct task_struct * (*pick_next_task) (struct rq *rq, struct task_struct *prev); void (*put_prev_task) (struct rq *rq, struct task_struct *p); int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags); void (*migrate_task_rq)(struct task_struct *p, int next_cpu); void (*post_schedule) (struct rq *this_rq); void (*task_waking) (struct task_struct *task); void (*task_woken) (struct rq *this_rq, struct task_struct *task); void (*set_cpus_allowed)(struct task_struct *p, const struct cpumask *newmask);
  15. 15. struct sched_class Copyright Hitachi Ltd. 2014. All rights reserved. 15 ! void (*rq_online)(struct rq *rq); void (*rq_offline)(struct rq *rq); void (*set_curr_task) (struct rq *rq); void (*task_tick) (struct rq *rq, struct task_struct *p, int queued); void (*task_fork) (struct task_struct *p); void (*task_dead) (struct task_struct *p); void (*switched_from) (struct rq *this_rq, struct task_struct *task); void (*switched_to) (struct rq *this_rq, struct task_struct *task); void (*prio_changed) (struct rq *this_rq, struct task_struct *task, int oldprio); unsigned int (*get_rr_interval) (struct rq *rq, struct task_struct *task); void (*task_move_group) (struct task_struct *p, int on_rq); };
  16. 16. struct rq • CPUごとに存在するランキューを表す構造体 • ランキュー実体は各クラスのランキュー構造体 Copyright Hitachi Ltd. 2014. All rights reserved. 16 型名前説明 raw_spinlock_t lock ランキュー自体を保護するロック unsigned int nr_running ランキュー内のTASK_RUNNING状態のプロセ ス数 struct cfs_rq cfs fairクラスのランキュー struct rt_rq rt rtクラスのランキュー struct dl_rq dl deadlineクラスのランキュー struct task_struct * curr カレントプロセス u64 clock ランキュー操作時刻 int cpu このランキューの存在するCPU
  17. 17. スケジューラ関数 • scheduler_tick(void), hrtick(void) • タイマーtick割り込み時に呼び出され、スケジューリング・ クラスごとのtick時処理(アカウンティングなど)を行う • try_to_wake_up(p, state, wake_flags) • プロセスpがstateの条件に合致するならpを起床させる • schedule(void) • 次に実行するプロセスを選択し、プロセス切り替えを行う Copyright Hitachi Ltd. 2014. All rights reserved. 17
  18. 18. scheduler_tick() (x86における)呼び出し経路 • smp_apic_timer_interrupt() • local_apic_timer_interrupt() • hrtimer_interrupt() • __run_hrtimer() • tick_sched_timer() • tick_sched_handle() • update_process_times() • scheduler_tick() Copyright Hitachi Ltd. 2014. All rights reserved. 18
  19. 19. void scheduler_tick(void) { int cpu = smp_processor_id(); struct rq *rq = cpu_rq(cpu); struct task_struct *curr = rq->curr; ! sched_clock_tick(); ! raw_spin_lock(&rq->lock); update_rq_clock(rq); curr->sched_class->task_tick(rq, curr, 0); update_cpu_load_active(rq); raw_spin_unlock(&rq->lock); ! perf_event_task_tick(); ! #ifdef CONFIG_SMP rq->idle_balance = idle_cpu(cpu); trigger_load_balance(rq); #endif rq_last_tick_reset(rq); } (スケジューラで利用する時刻情報の 取得関数である)sched_clock()が不安 定な環境のためにsched_clock_dataを 更新 • idle中にHWカウンタが止まるケース • 周波数の動的な変更でHWカウンタの 進む速度が変わるケース ランキューのデータを保護するため スピンロックを取得 (割り込みコンテキストなのでraw_) プロセスのアカウンティングに使う ランキュー操作時刻を更新 • さらにCPUごとの割り込み処理時 間&仮想マシン実行時間の統計情 Copyright Hitachi Ltd. 2013. All rights reserved. 19
  20. 20. void scheduler_tick(void) { int cpu = smp_processor_id(); struct rq *rq = cpu_rq(cpu); struct task_struct *curr = rq->curr; ! sched_clock_tick(); ! raw_spin_lock(&rq->lock); update_rq_clock(rq); curr->sched_class->task_tick(rq, curr, 0); update_cpu_load_active(rq); raw_spin_unlock(&rq->lock); ! perf_event_task_tick(); ! #ifdef CONFIG_SMP rq->idle_balance = idle_cpu(cpu); trigger_load_balance(rq); #endif rq_last_tick_reset(rq); } カレントプロセスの属するスケジュー リングクラスのtask_tick()メソッドを 呼び出す。 • プロセスのアカウンティング(タイ ムスライスの更新など) • プロセス切替え(リスケジューリン グ)の必要性の確認 • etc. Copyright Hitachi Ltd. 2013. All rights reserved. 20
  21. 21. void scheduler_tick(void) { int cpu = smp_processor_id(); struct rq *rq = cpu_rq(cpu); struct task_struct *curr = rq->curr; ! sched_clock_tick(); ! raw_spin_lock(&rq->lock); update_rq_clock(rq); curr->sched_class->task_tick(rq, curr, 0); update_cpu_load_active(rq); raw_spin_unlock(&rq->lock); ! perf_event_task_tick(); ! #ifdef CONFIG_SMP rq->idle_balance = idle_cpu(cpu); trigger_load_balance(rq); #endif rq_last_tick_reset(rq); } CPU負荷の計算 ロードバランスの開始要求 (raise_softirq(SCHED_SOFTIRQ)) tick割り込み終了後に開始される Copyright Hitachi Ltd. 2013. All rights reserved. 21
  22. 22. Copyright Hitachi Ltd. 2014. All rights reserved. hrtick() • 通常のtickは1msおき(HZ=1000の場合) • プロセス切替は、カレントプロセスがタイムスライス を使い切った後のtickで実行される • タイムスライスを使い切るタイミングでtickを上 げることで高精度な時分割を実現 • HRTICK機能がONの場合(/sys/kernel/debug/sched_features) 22
  23. 23. static enum hrtimer_restart hrtick(struct hrtimer *timer) { struct rq *rq = container_of(timer, struct rq, hrtick_timer); ! WARN_ON_ONCE(cpu_of(rq) != smp_processor_id()); ! raw_spin_lock(&rq->lock); update_rq_clock(rq); rq->curr->sched_class->task_tick(rq, rq->curr, 1); raw_spin_unlock(&rq->lock); ! return HRTIMER_NORESTART; } task_tickの第3引数(int queue)で、 hrtick()からの呼び出しであることを 伝える(=queued tick) • プリエンプトすべきかどうかのチェッ クを省略する Copyright Hitachi Ltd. 2013. All rights reserved. 23
  24. 24. try_to_wake_up() • 下記の3関数から呼ばれる • wake_up_process() • TASK_{UN}INTERRUPTIBLEのプロセスのみ起床 • wake_up_state() • 指定した状態のプロセスのみ起床 • default_wake_function() • wait queue等のコールバックとして使われる • wake_flagsを使用(今回は未調査) Copyright Hitachi Ltd. 2014. All rights reserved. 24
  25. 25. static int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) { unsigned long flags; int cpu, success = 0; ! smp_mb__before_spinlock(); raw_spin_lock_irqsave(&p->pi_lock, flags); if (!(p->state & state)) goto out; ! success = 1; /*we're going to change ->state*/ cpu = task_cpu(p); ! if (p->on_rq && ttwu_remote(p, wake_flags)) goto stat; p: 起床させるプロセス state: pがどの状態であれば起床させ るかの条件 wake_flags: プロセスpが指定されたstateでない なら終了 プロセスpがまだランキューに残ってい る場合、ttwu_remoteを呼び出してラ ンキュー操作の不要な軽量起床を行う 1. pと起床先CPUのカレントプロセスを 比較して必要ならプリエンプト要求 を出す 2. pの状態をTASK_RUNNINGへ戻す 3. pが起床先CPUですぐに実行できな い場合、他CPUで実行できるか試み る(SMPかつrt, deadlineクラスのみ) Copyright Hitachi Ltd. 2014. All rights reserved. 25
  26. 26. #ifdef CONFIG_SMP while (p->on_cpu) cpu_relax(); 起床先CPUで(ランキューから外さ れている)プロセスpが別のプロセス へ切り替わるのを待つ smp_rmb(); p->sched_contributes_to_load = !!task_contributes_to_load(p); p->state = TASK_WAKING; ! if (p->sched_class->task_waking) p->sched_class->task_waking(p); プロセスpの状態をWAKING(起床中)に変更 プロセスpの所属クラスの task_waking()メソッド呼び出し • fairクラスのみ: 仮想実行時間 (vruntime)の補充 cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags); if (task_cpu(p) != cpu) { wake_flags |= WF_MIGRATED; set_task_cpu(p, cpu); } #endif /* CONFIG_SMP */ ! ttwu_queue(p, cpu); stat: ttwu_stat(p, cpu, wake_flags); out: raw_spin_unlock_irqrestore(&p->pi_lock, flags); return success; } Copyright Hitachi Ltd. 2014. All rights reserved. 26
  27. 27. #ifdef CONFIG_SMP while (p->on_cpu) cpu_relax(); smp_rmb(); p->sched_contributes_to_load = !!task_contributes_to_load(p); p->state = TASK_WAKING; ! if (p->sched_class->task_waking) p->sched_class->task_waking(p); プロセスpの所属クラスの select_task_rq()メソッドを呼び 出し、起床先CPUを決定 cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags); if (task_cpu(p) != cpu) { wake_flags |= WF_MIGRATED; set_task_cpu(p, cpu); } #endif /* CONFIG_SMP */ ! ttwu_queue(p, cpu); stat: ttwu_stat(p, cpu, wake_flags); out: raw_spin_unlock_irqrestore(&p->pi_lock, flags); return success; } プロセスpの元の動作CPUと起床 先CPUが異なる場合、pの動作CPU を新たにセットする Copyright Hitachi Ltd. 2014. All rights reserved. 27
  28. 28. #ifdef CONFIG_SMP while (p->on_cpu) cpu_relax(); smp_rmb(); p->sched_contributes_to_load = !!task_contributes_to_load(p); p->state = TASK_WAKING; ! 基本パス 1. プロセスpをランキューへ入れる 2. pと起床先CPUのカレントプロセスを比較して必要 ならプリエンプト要求を出す 3. pの状態をTASK_RUNNINGへ戻す 4. pが起床先CPUですぐに実行できない場合、他CPU で実行できるか試みる(SMPかつrt, deadlineクラス のみ) ! TTWU_QUEUE機能がON、かつ、起床元CPUと起床先 CPUがlast-levelキャッシュを共有していない場合 1. 起床先CPUランキューのwake_listへ繋ぐ 2. rescheduling IPIを起床先CPUへ送る →CPU間でランキューのロック競合を起こさずに済む if (p->sched_class->task_waking) p->sched_class->task_waking(p); cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags); if (task_cpu(p) != cpu) { wake_flags |= WF_MIGRATED; set_task_cpu(p, cpu); } #endif /* CONFIG_SMP */ ! ttwu_queue(p, cpu); stat: ttwu_stat(p, cpu, wake_flags); out: raw_spin_unlock_irqrestore(&p->pi_lock, flags); return success; } Copyright Hitachi Ltd. 2014. All rights reserved. 28
  29. 29. schedule() • 明示的な呼び出し • ミューテックス、セマフォ、wait-queue • プリエンプションによる呼び出し • カレントプロセスにTIF_NEED_RESCHEDフラグが 立っている状態でチェックポイント(後述)を実行 した時 Copyright Hitachi Ltd. 2014. All rights reserved. 29
  30. 30. TIF_NEED_RESCHEDの チェックポイント • CONFIG_PREEMPT=y の時 • システムコールや例外中にプリエンプションが許可された時 • 割り込みハンドラ終了時 • CONFIG_PREEMPT is not set の時 • cond_resched()が呼ばれた時 • システムコール/例外/割り込みからユーザ空間へ戻る時 Copyright Hitachi Ltd. 2014. All rights reserved. 30
  31. 31. schedule() Copyright Hitachi Ltd. 2014. All rights reserved. 31 asmlinkage __visible void __sched schedule(void) { struct task_struct *tsk = current; ! sched_submit_work(tsk); __schedule(); } スリープする前にキューイングして おいたブロックI/Oをsubmitする
  32. 32. static void __sched __schedule(void) { struct task_struct *prev, *next; unsigned long *switch_count; struct rq *rq; int cpu; ! need_resched: preempt_disable(); cpu = smp_processor_id(); rq = cpu_rq(cpu); rcu_note_context_switch(cpu); prev = rq->curr; ! schedule_debug(prev); ! if (sched_feat(HRTICK)) hrtick_clear(rq); ! smp_mb__before_spinlock(); raw_spin_lock_irq(&rq->lock); プリエンプションがネストしないよ うに無効化しておく prev(切り替え前)プロセス =カレントプロセス ランキューのロック獲得 Copyright Hitachi Ltd. 2014. All rights reserved. 32
  33. 33. switch_count = &prev->nivcsw; if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { if (unlikely(signal_pending_state(prev->state, prev))) { prev->state = TASK_RUNNING; } else { prevプロセスがTASK_RUNNINGで ない && 明示的な__schedule()呼び出し 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; } ! if (prev->on_rq || rq->skip_clock_update < 0) update_rq_clock(rq); Copyright Hitachi Ltd. 2014. All rights reserved. 33
  34. 34. switch_count = &prev->nivcsw; if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { if (unlikely(signal_pending_state(prev->state, prev))) { prev->state = TASK_RUNNING; } else { prevプロセスにペンディング中のシ グナルがある場合、prevを再度 RUNNING状態へ戻す 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; } ! if (prev->on_rq || rq->skip_clock_update < 0) update_rq_clock(rq); Copyright Hitachi Ltd. 2014. All rights reserved. 34
  35. 35. 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; ! シグナルペンディング中でないなら、 prevプロセスをランキューから外す to_wakeup = wq_worker_sleeping(prev, cpu); if (to_wakeup) try_to_wake_up_local(to_wakeup); } } switch_count = &prev->nvcsw; } ! if (prev->on_rq || rq->skip_clock_update < 0) update_rq_clock(rq); Copyright Hitachi Ltd. 2014. All rights reserved. 35
  36. 36. next = pick_next_task(rq, prev); clear_tsk_need_resched(prev); clear_preempt_need_resched(); rq->skip_clock_update = 0; ! if (likely(prev != next)) { 全スケジューリングクラスのプロセ スのうち、最も優先度の高いものを nextプロセスとして選択 prev != nextならプロセス切り替えへ入る。 カレントプロセスをnextに変更。 rq->nr_switches++; rq->curr = next; ++*switch_count; context_switch(rq, prev, next); /* unlocks the rq */ ! cpu = smp_processor_id(); rq = cpu_rq(cpu); } else raw_spin_unlock_irq(&rq->lock); ! post_schedule(rq); ! sched_preempt_enable_no_resched(); if (need_resched()) goto need_resched; } プロセス間のコンテキストを切り替える。 戻ってきた時にはrq->lockはunlockされて いる。 カーネルスタックが切り替わっているので、 ローカル変数を更新 Copyright Hitachi Ltd. 2014. All rights reserved. 36
  37. 37. next = pick_next_task(rq, prev); clear_tsk_need_resched(prev); clear_preempt_need_resched(); rq->skip_clock_update = 0; ! if (likely(prev != next)) { rq->nr_switches++; rq->curr = next; ++*switch_count; context_switch(rq, prev, next); /* unlocks the rq */ ! cpu = smp_processor_id(); rq = cpu_rq(cpu); } else raw_spin_unlock_irq(&rq->lock); ! post_schedule(rq); ! sched_preempt_enable_no_resched(); if (need_resched()) goto need_resched; } 各スケジューリングクラスのプロセス切り 替え後に実行すべき処理を実行 • rt, deadlineクラスのみ: カレントプロセ ス以外で優先度の高いプロセスを他のCPU で実行できないか試みる • システム全体で優先度順になるように プリエンプションを有効化する。 ただし、__schedule()をネストさ せないようにプリエンプション処 理は関数内のループにより行う Copyright Hitachi Ltd. 2014. All rights reserved. 37

×