SlideShare a Scribd company logo
1 of 37
Download to read offline
Linuxのプロセススケジューラ 
(Reading the Linux process scheduler) 
Copyright Hitachi Ltd. 2014. All rights reserved. 
日立製作所 横浜研究所 
豊岡 拓 (hiraku.toyooka.gu@hitachi.com) 
! 
Linux 3.15.0版
プロセススケジューラに関す 
るトピックの全体像 
プロセススケジューラ 
スケジューリング・クラス 
CFS Real-time 
Copyright Hitachi Ltd. 2014. All rights reserved. 
Dead-line 
ロードバランスグループ・ 
スケジューリング 
カーネル内 
プリエンプション 
stop 
idle 
省電力 
割り込み, 
スピンロック 
プロセス/スレッド, 
時間管理, 
高精度タイマ, 
tick管理, 
etc.. 
wait-queue, 
セマフォ, 
ミューテックス, 
依存 
ユーザ空間へのI/F(システムコール等) etc.. 
2 
共通部(優先度、データ構造、関数)
プロセススケジューリングとは 
• 複数のプロセスに対して、どれだけのCPU時間を、どのよう 
な順序で割り当てるかを決める方法 
• 目標(全部を同時には満たせない) 
• 高スループット 
• 低レイテンシ 
• 公平性の実現 
• 実時間制約を満たす 
• 省電力 
Copyright Hitachi Ltd. 2014. All rights reserved. 
3
スケジューリングクラスとポリシー 
• 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
Fairクラス(CFS) 
• プロセス間の公平性を保ちつつ、CPU利用効率の最大化とイベント処理へ 
のレスポンス高速化を両立 
• 累積実行時間が最も少ないプロセスにCPUを割り当てる 
• ただし、実行可能プロセス数が多い時にプロセス切り替えが頻繁に起こ 
るのを防ぐため、最小のタイムスライスを持つ 
• SCHED_OTHER: デフォルトのポリシー 
• SCHED_BATCH: CPU-intensiveだと仮定してスイッチを起こしにくくする 
• SCHED_IDLE: 他に動けるプロセスがいない時だけ実行される 
Copyright Hitachi Ltd. 2014. All rights reserved. 
5
Real-timeクラス 
• 静的に与えられた優先度の高い順にプロセスをスケジュー 
ル 
• SCHED_FIFO 
• 自発的にCPUを手放さない限り実行し続ける 
• SCHED_RR 
• 最高優先度のプロセスが複数ある場合、与えられた 
タイムスライスによってラウンドロビン実行する 
Copyright Hitachi Ltd. 2014. All rights reserved. 
6
Deadlineクラス 
• SCHED_DEADLINE 
• 処理周期、期限、最大実行時間をプロセスに 
与え、期限の早いプロセスから順番にスケ 
ジュール 
Copyright Hitachi Ltd. 2014. All rights reserved. 
7
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 - - - 
高い 
低い
データ構造 
• プロセス構造体 (struct task_struct) 
• プロセスの状態フラグ 
• スケジューリング・クラス (struct sched_class) 
• ランキュー (struct rq) 
Copyright Hitachi Ltd. 2014. All rights reserved. 
9
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
プロセスの状態フラグ 
フラグ説明 
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 起床中
プロセスの一生 
(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
struct sched_class 
• スケジューリングクラスを表す構造体 
• クラス固有処理の関数ポインタの表 
• スケジューラ共通処理からクラス固有処理への 
呼び出しインタフェース 
Copyright Hitachi Ltd. 2014. All rights reserved. 
13
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);
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); 
};
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
スケジューラ関数 
• 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
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
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
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
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
Copyright Hitachi Ltd. 2014. All rights reserved. 
hrtick() 
• 通常のtickは1msおき(HZ=1000の場合) 
• プロセス切替は、カレントプロセスがタイムスライス 
を使い切った後のtickで実行される 
• タイムスライスを使い切るタイミングでtickを上 
げることで高精度な時分割を実現 
• HRTICK機能がONの場合(/sys/kernel/debug/sched_features) 
22
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
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
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
#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
#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
#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
schedule() 
• 明示的な呼び出し 
• ミューテックス、セマフォ、wait-queue 
• プリエンプションによる呼び出し 
• カレントプロセスにTIF_NEED_RESCHEDフラグが 
立っている状態でチェックポイント(後述)を実行 
した時 
Copyright Hitachi Ltd. 2014. All rights reserved. 
29
TIF_NEED_RESCHEDの 
チェックポイント 
• CONFIG_PREEMPT=y の時 
• システムコールや例外中にプリエンプションが許可された時 
• 割り込みハンドラ終了時 
• CONFIG_PREEMPT is not set の時 
• cond_resched()が呼ばれた時 
• システムコール/例外/割り込みからユーザ空間へ戻る時 
Copyright Hitachi Ltd. 2014. All rights reserved. 
30
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する
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
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
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
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
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
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

More Related Content

What's hot

あるコンテキストスイッチの話
あるコンテキストスイッチの話あるコンテキストスイッチの話
あるコンテキストスイッチの話nullnilaki
 
Completely Fair Scheduler (CFS)
Completely Fair Scheduler (CFS)Completely Fair Scheduler (CFS)
Completely Fair Scheduler (CFS)gokzy
 
KVM環境におけるネットワーク速度ベンチマーク
KVM環境におけるネットワーク速度ベンチマークKVM環境におけるネットワーク速度ベンチマーク
KVM環境におけるネットワーク速度ベンチマークVirtualTech Japan Inc.
 
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)NTT DATA Technology & Innovation
 
はりぼて OS で ELF なアプリを起動してみた
はりぼて OS で ELF なアプリを起動してみたはりぼて OS で ELF なアプリを起動してみた
はりぼて OS で ELF なアプリを起動してみたuchan_nos
 
できる!並列・並行プログラミング
できる!並列・並行プログラミングできる!並列・並行プログラミング
できる!並列・並行プログラミングPreferred Networks
 
冬のLock free祭り safe
冬のLock free祭り safe冬のLock free祭り safe
冬のLock free祭り safeKumazaki Hiroki
 
マルチコアを用いた画像処理
マルチコアを用いた画像処理マルチコアを用いた画像処理
マルチコアを用いた画像処理Norishige Fukushima
 
マルチコアとネットワークスタックの高速化技法
マルチコアとネットワークスタックの高速化技法マルチコアとネットワークスタックの高速化技法
マルチコアとネットワークスタックの高速化技法Takuya ASADA
 
Slurmのジョブスケジューリングと実装
Slurmのジョブスケジューリングと実装Slurmのジョブスケジューリングと実装
Slurmのジョブスケジューリングと実装Ryuichi Sakamoto
 
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門Fixstars Corporation
 
不揮発メモリ(NVDIMM)とLinuxの対応動向について(for comsys 2019 ver.)
不揮発メモリ(NVDIMM)とLinuxの対応動向について(for comsys 2019 ver.)不揮発メモリ(NVDIMM)とLinuxの対応動向について(for comsys 2019 ver.)
不揮発メモリ(NVDIMM)とLinuxの対応動向について(for comsys 2019 ver.)Yasunori Goto
 
10GbE時代のネットワークI/O高速化
10GbE時代のネットワークI/O高速化10GbE時代のネットワークI/O高速化
10GbE時代のネットワークI/O高速化Takuya ASADA
 
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編Fixstars Corporation
 
エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)
エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)
エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)Takeshi HASEGAWA
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?Moriharu Ohzu
 

What's hot (20)

あるコンテキストスイッチの話
あるコンテキストスイッチの話あるコンテキストスイッチの話
あるコンテキストスイッチの話
 
Completely Fair Scheduler (CFS)
Completely Fair Scheduler (CFS)Completely Fair Scheduler (CFS)
Completely Fair Scheduler (CFS)
 
KVM環境におけるネットワーク速度ベンチマーク
KVM環境におけるネットワーク速度ベンチマークKVM環境におけるネットワーク速度ベンチマーク
KVM環境におけるネットワーク速度ベンチマーク
 
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
 
はりぼて OS で ELF なアプリを起動してみた
はりぼて OS で ELF なアプリを起動してみたはりぼて OS で ELF なアプリを起動してみた
はりぼて OS で ELF なアプリを起動してみた
 
できる!並列・並行プログラミング
できる!並列・並行プログラミングできる!並列・並行プログラミング
できる!並列・並行プログラミング
 
Glibc malloc internal
Glibc malloc internalGlibc malloc internal
Glibc malloc internal
 
冬のLock free祭り safe
冬のLock free祭り safe冬のLock free祭り safe
冬のLock free祭り safe
 
マルチコアを用いた画像処理
マルチコアを用いた画像処理マルチコアを用いた画像処理
マルチコアを用いた画像処理
 
マルチコアとネットワークスタックの高速化技法
マルチコアとネットワークスタックの高速化技法マルチコアとネットワークスタックの高速化技法
マルチコアとネットワークスタックの高速化技法
 
Slurmのジョブスケジューリングと実装
Slurmのジョブスケジューリングと実装Slurmのジョブスケジューリングと実装
Slurmのジョブスケジューリングと実装
 
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
 
不揮発メモリ(NVDIMM)とLinuxの対応動向について(for comsys 2019 ver.)
不揮発メモリ(NVDIMM)とLinuxの対応動向について(for comsys 2019 ver.)不揮発メモリ(NVDIMM)とLinuxの対応動向について(for comsys 2019 ver.)
不揮発メモリ(NVDIMM)とLinuxの対応動向について(for comsys 2019 ver.)
 
Raft
RaftRaft
Raft
 
eBPFを用いたトレーシングについて
eBPFを用いたトレーシングについてeBPFを用いたトレーシングについて
eBPFを用いたトレーシングについて
 
ゼロからはじめるKVM超入門
ゼロからはじめるKVM超入門ゼロからはじめるKVM超入門
ゼロからはじめるKVM超入門
 
10GbE時代のネットワークI/O高速化
10GbE時代のネットワークI/O高速化10GbE時代のネットワークI/O高速化
10GbE時代のネットワークI/O高速化
 
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
 
エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)
エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)
エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
 

Viewers also liked

スケジューラからみたOSの話
スケジューラからみたOSの話スケジューラからみたOSの話
スケジューラからみたOSの話Takeshi Fujiwara
 
First step for dynticks in FreeBSD
First step for dynticks in FreeBSDFirst step for dynticks in FreeBSD
First step for dynticks in FreeBSDTsuyoshi OZAWA
 
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)Yahoo!デベロッパーネットワーク
 
Basic of virtual memory of Linux
Basic of virtual memory of LinuxBasic of virtual memory of Linux
Basic of virtual memory of LinuxTetsuyuki Kobayashi
 
x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチMasami Ichikawa
 
0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきことmao999
 
深層学習ライブラリのプログラミングモデル
深層学習ライブラリのプログラミングモデル深層学習ライブラリのプログラミングモデル
深層学習ライブラリのプログラミングモデルYuta Kashino
 
Linuxカーネル超入門
Linuxカーネル超入門Linuxカーネル超入門
Linuxカーネル超入門Takashi Masuda
 

Viewers also liked (8)

スケジューラからみたOSの話
スケジューラからみたOSの話スケジューラからみたOSの話
スケジューラからみたOSの話
 
First step for dynticks in FreeBSD
First step for dynticks in FreeBSDFirst step for dynticks in FreeBSD
First step for dynticks in FreeBSD
 
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)
 
Basic of virtual memory of Linux
Basic of virtual memory of LinuxBasic of virtual memory of Linux
Basic of virtual memory of Linux
 
x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチ
 
0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと
 
深層学習ライブラリのプログラミングモデル
深層学習ライブラリのプログラミングモデル深層学習ライブラリのプログラミングモデル
深層学習ライブラリのプログラミングモデル
 
Linuxカーネル超入門
Linuxカーネル超入門Linuxカーネル超入門
Linuxカーネル超入門
 

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

【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門 【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門 sandai
 
Ylug 110th kpatch code reading
Ylug 110th kpatch code readingYlug 110th kpatch code reading
Ylug 110th kpatch code readingMasami Hiramatsu
 
Boost9 session
Boost9 sessionBoost9 session
Boost9 sessionfreedom404
 
システムパフォーマンス勉強会#5
システムパフォーマンス勉強会#5システムパフォーマンス勉強会#5
システムパフォーマンス勉強会#5shingo suzuki
 
2011.09.18 v7から始めるunix まとめ
2011.09.18 v7から始めるunix まとめ2011.09.18 v7から始めるunix まとめ
2011.09.18 v7から始めるunix まとめMakiko Konoshima
 
プロセスとコンテキストスイッチ
プロセスとコンテキストスイッチプロセスとコンテキストスイッチ
プロセスとコンテキストスイッチKazuki Onishi
 
Spmv9forpublic
Spmv9forpublicSpmv9forpublic
Spmv9forpublicT2C_
 
リアルタイム処理エンジン Gearpumpの紹介
リアルタイム処理エンジンGearpumpの紹介リアルタイム処理エンジンGearpumpの紹介
リアルタイム処理エンジン Gearpumpの紹介Sotaro Kimura
 
Linux Performance Analysis in 15 minutes
Linux Performance Analysis in 15 minutesLinux Performance Analysis in 15 minutes
Linux Performance Analysis in 15 minutesYohei Azekatsu
 
Unixカーネルの設計 7 プロセスの制御
Unixカーネルの設計 7 プロセスの制御Unixカーネルの設計 7 プロセスの制御
Unixカーネルの設計 7 プロセスの制御Norito Agetsuma
 
Python で munin plugin を書いてみる
Python で munin plugin を書いてみるPython で munin plugin を書いてみる
Python で munin plugin を書いてみるftnk
 
プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜京大 マイコンクラブ
 
HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLHandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLakirahiguchi
 
2012研究室紹介(大川)
2012研究室紹介(大川)2012研究室紹介(大川)
2012研究室紹介(大川)猛 大川
 
C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml ssuser3a4b8c
 
Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.ContextAkira Takahashi
 
Kernel vm study_2_xv6_scheduler_part1_revised
Kernel vm study_2_xv6_scheduler_part1_revisedKernel vm study_2_xv6_scheduler_part1_revised
Kernel vm study_2_xv6_scheduler_part1_revisedToshiaki Nozawa
 
PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門Yosuke Onoue
 

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

【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門 【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門
 
Ylug 110th kpatch code reading
Ylug 110th kpatch code readingYlug 110th kpatch code reading
Ylug 110th kpatch code reading
 
Boost9 session
Boost9 sessionBoost9 session
Boost9 session
 
SystemV IPC
SystemV IPCSystemV IPC
SystemV IPC
 
システムパフォーマンス勉強会#5
システムパフォーマンス勉強会#5システムパフォーマンス勉強会#5
システムパフォーマンス勉強会#5
 
2011.09.18 v7から始めるunix まとめ
2011.09.18 v7から始めるunix まとめ2011.09.18 v7から始めるunix まとめ
2011.09.18 v7から始めるunix まとめ
 
プロセスとコンテキストスイッチ
プロセスとコンテキストスイッチプロセスとコンテキストスイッチ
プロセスとコンテキストスイッチ
 
Spmv9forpublic
Spmv9forpublicSpmv9forpublic
Spmv9forpublic
 
リアルタイム処理エンジン Gearpumpの紹介
リアルタイム処理エンジンGearpumpの紹介リアルタイム処理エンジンGearpumpの紹介
リアルタイム処理エンジン Gearpumpの紹介
 
Linux Performance Analysis in 15 minutes
Linux Performance Analysis in 15 minutesLinux Performance Analysis in 15 minutes
Linux Performance Analysis in 15 minutes
 
Unixカーネルの設計 7 プロセスの制御
Unixカーネルの設計 7 プロセスの制御Unixカーネルの設計 7 プロセスの制御
Unixカーネルの設計 7 プロセスの制御
 
Python で munin plugin を書いてみる
Python で munin plugin を書いてみるPython で munin plugin を書いてみる
Python で munin plugin を書いてみる
 
プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜
 
HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLHandlerSocket plugin for MySQL
HandlerSocket plugin for MySQL
 
2012研究室紹介(大川)
2012研究室紹介(大川)2012研究室紹介(大川)
2012研究室紹介(大川)
 
C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml
 
Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.Context
 
Kernel vm study_2_xv6_scheduler_part1_revised
Kernel vm study_2_xv6_scheduler_part1_revisedKernel vm study_2_xv6_scheduler_part1_revised
Kernel vm study_2_xv6_scheduler_part1_revised
 
Boost Tour 1.50.0 All
Boost Tour 1.50.0 AllBoost Tour 1.50.0 All
Boost Tour 1.50.0 All
 
PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門
 

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

  • 1. Linuxのプロセススケジューラ (Reading the Linux process scheduler) Copyright Hitachi Ltd. 2014. All rights reserved. 日立製作所 横浜研究所 豊岡 拓 (hiraku.toyooka.gu@hitachi.com) ! Linux 3.15.0版
  • 2. プロセススケジューラに関す るトピックの全体像 プロセススケジューラ スケジューリング・クラス CFS Real-time Copyright Hitachi Ltd. 2014. All rights reserved. Dead-line ロードバランスグループ・ スケジューリング カーネル内 プリエンプション stop idle 省電力 割り込み, スピンロック プロセス/スレッド, 時間管理, 高精度タイマ, tick管理, etc.. wait-queue, セマフォ, ミューテックス, 依存 ユーザ空間へのI/F(システムコール等) etc.. 2 共通部(優先度、データ構造、関数)
  • 3. プロセススケジューリングとは • 複数のプロセスに対して、どれだけのCPU時間を、どのよう な順序で割り当てるかを決める方法 • 目標(全部を同時には満たせない) • 高スループット • 低レイテンシ • 公平性の実現 • 実時間制約を満たす • 省電力 Copyright Hitachi Ltd. 2014. All rights reserved. 3
  • 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. Fairクラス(CFS) • プロセス間の公平性を保ちつつ、CPU利用効率の最大化とイベント処理へ のレスポンス高速化を両立 • 累積実行時間が最も少ないプロセスにCPUを割り当てる • ただし、実行可能プロセス数が多い時にプロセス切り替えが頻繁に起こ るのを防ぐため、最小のタイムスライスを持つ • SCHED_OTHER: デフォルトのポリシー • SCHED_BATCH: CPU-intensiveだと仮定してスイッチを起こしにくくする • SCHED_IDLE: 他に動けるプロセスがいない時だけ実行される Copyright Hitachi Ltd. 2014. All rights reserved. 5
  • 6. Real-timeクラス • 静的に与えられた優先度の高い順にプロセスをスケジュー ル • SCHED_FIFO • 自発的にCPUを手放さない限り実行し続ける • SCHED_RR • 最高優先度のプロセスが複数ある場合、与えられた タイムスライスによってラウンドロビン実行する Copyright Hitachi Ltd. 2014. All rights reserved. 6
  • 7. Deadlineクラス • SCHED_DEADLINE • 処理周期、期限、最大実行時間をプロセスに 与え、期限の早いプロセスから順番にスケ ジュール Copyright Hitachi Ltd. 2014. All rights reserved. 7
  • 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. データ構造 • プロセス構造体 (struct task_struct) • プロセスの状態フラグ • スケジューリング・クラス (struct sched_class) • ランキュー (struct rq) Copyright Hitachi Ltd. 2014. All rights reserved. 9
  • 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. プロセスの状態フラグ フラグ説明 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. プロセスの一生 (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. struct sched_class • スケジューリングクラスを表す構造体 • クラス固有処理の関数ポインタの表 • スケジューラ共通処理からクラス固有処理への 呼び出しインタフェース Copyright Hitachi Ltd. 2014. All rights reserved. 13
  • 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. 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. 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. スケジューラ関数 • 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. 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. 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. 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. 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. Copyright Hitachi Ltd. 2014. All rights reserved. hrtick() • 通常のtickは1msおき(HZ=1000の場合) • プロセス切替は、カレントプロセスがタイムスライス を使い切った後のtickで実行される • タイムスライスを使い切るタイミングでtickを上 げることで高精度な時分割を実現 • HRTICK機能がONの場合(/sys/kernel/debug/sched_features) 22
  • 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. 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. 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. #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. #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. #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. schedule() • 明示的な呼び出し • ミューテックス、セマフォ、wait-queue • プリエンプションによる呼び出し • カレントプロセスにTIF_NEED_RESCHEDフラグが 立っている状態でチェックポイント(後述)を実行 した時 Copyright Hitachi Ltd. 2014. All rights reserved. 29
  • 30. TIF_NEED_RESCHEDの チェックポイント • CONFIG_PREEMPT=y の時 • システムコールや例外中にプリエンプションが許可された時 • 割り込みハンドラ終了時 • CONFIG_PREEMPT is not set の時 • cond_resched()が呼ばれた時 • システムコール/例外/割り込みからユーザ空間へ戻る時 Copyright Hitachi Ltd. 2014. All rights reserved. 30
  • 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. 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. 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. 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. 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. 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. 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