Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
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

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