プリエンプション
Rev.5を表示中。最新版はこちら。
CONFIG_PREEMPTを定義しておくと、プロセスコンテキストでカーネル内のコードを実行している時でも他のプロセスに切りかわることができる。CONFIG_PREEMPTがなければLinux 2.4と同様に明示的に自分からSleepしてCPUを手放さない限り切り替わらない。
通常Unixはユーザモードのみプリエンプティブでカーネルモードではノンプリエンプティブだが、CONFIG_PREEMPTを有効にするとカーネルモードでもプリエンプティブになる。
preempt_schedule()
プリエンプションを行なう。
preempt_enable()から呼ばれる
preempt_enable()から呼ばれる
preempt_schedule()
{
/* Preemption可能かチェック */
if (unlikely(ti->preempt_count || irqs_disabled()))
return;
need_resched:
add_preempt_count(PREEMPT_ACTIVE);
/* preempt_countにPREEMPT_ACTIVEフラグを立てる */
schedule();
sub_preempt_count(PREEMPT_ACTIVE);
/* プリエンプション前に立てられたフラグを落す */
barrier();
if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))
goto need_resched;
}
[関連関数]
preempt_disable()
プリエンプションを禁止にする。
inc_preempt_count()でthread_info.preempt_countをインクリメントしている。
preempt_enable()
プリエンプションを許可する。
プリエンプションが必要であればついでにプリエンプションも行なわれる。
プリエンプションが必要であればついでにプリエンプションも行なわれる。
dec_preempt_count()でpreemption_countをデクリメント後、preempt_check_resched()を呼んでプリエンプションが必要なら(TIF_NEED_RESCHED立っていたら)プリエンプションを実行している。
preempt_schedule_irq() - 割り込みコンテキストからプリエンプトする場合こちらが使用される
(割り込み/例外からカーネルモードにもどる際、呼ばれる)
処理内容はpreempt_schedule()とほぼ同じ。schedule()を呼ぶ際は、割り込みを禁止にしている。割り込みが発生して再帰的に呼び出されるのを防ぐため。
entry.S::resume_kernel: - 割り込みや例外からカーネルモードに戻る時に呼ばれる
preempt_countが0でなければPreemptDisable中なので、そのまま割り込み/例外から復帰。(復帰先はカーネルモード)
TIF_NEED_RESCHEDが立っていたらpreempt_schedule_irq()をコールしてPreempt
thread_info.preempt_count
bits 0-7 are the preemption count (max preemption depth: 256)
bits 8-15 are the softirq count (max # of softirqs: 256)
bits 16-23 are the hardirq count (max # of hardirqs: 256)
( bit 26 is the PREEMPT_ACTIVE flag. )
PREEMPT_MASK: 0x000000ff
SOFTIRQ_MASK: 0x0000ff00
HARDIRQ_MASK: 0x00ff0000
bits 8-15 are the softirq count (max # of softirqs: 256)
bits 16-23 are the hardirq count (max # of hardirqs: 256)
( bit 26 is the PREEMPT_ACTIVE flag. )
PREEMPT_MASK: 0x000000ff
SOFTIRQ_MASK: 0x0000ff00
HARDIRQ_MASK: 0x00ff0000
H/W割り込み処理時
S/W割り込み処理時
do_IRQ()でH/W割り込みの処理を開始するときにirq_enter()でHARDIRQ_MASKの部分が加算される。
S/W割り込み処理時
__do_softirq()でS/W割り込みの処理を開始するときにlocal_bh_disable()でSOFTIRQ_MASKの部分が加算される。
in_interrupt()で割り込み処理中かチェックするときも
current_thread_info()->preempt_countの
(HARDIRQ_MASK | SOFTIRQ_MASK)部分をチェックしている。
current_thread_info()->preempt_countの
(HARDIRQ_MASK | SOFTIRQ_MASK)部分をチェックしている。