カーネルプリエンプション


カーネルプリエンプションはカーネルパス下で、プロセスの切り替えを行う事をいいます。ただし、IO/セマフォ待ちとかで、プロセス切り替とは違います。例えば、カーネルはインターバルタイマーで、タイムシェアリングでプロセスの切り替えを行っています。タイマー割り込みは、プロセスがユーザモード/カーネルモードに関係なく発生します。プリエンプティブでないカーネルなら、タイマー割り込みがカーネルモードで発生したら、プロセスの切り替えは行いません。唯一ユーザモードで実行している時のみ、プロセス切り替えを行うことになります。プリエンプティブのカーネルなら、ユーザモード/カーネルモード関係なくプロセス切り替えを行うことが可能となります。(たぶん。)

プリエンプティブは、プロセス毎のthread_info構造体のpreempt_countで設定し、preempt_countが0の時、プリエンプティブされます。

割り込みが発生すると、アセンブラが書かれたラベルのinterruptへジャンプします。そこでは割り込み番号のチェック等を行った後、common_interruptへとジャンプします。

common_interruptで実際の割り込み処理は、do_IRQをコールすることで行っていて、その処理が終了するとret_from_intrへジャンプします。ここでカーネルプリエンプションのチェックが行われます。
common_interrupt:
       addl $-0x80,(%esp)      /* Adjust vector into the [-256,-1] range */
       SAVE_ALL
       TRACE_IRQS_OFF
       movl %esp,%eax
       call do_IRQ
       jmp ret_from_intr
ENDPROC(common_interrupt)
ret_from_intrで、割り込まれたプロセスのコンテキストはスタック上にあります。スタックから、割り込まれたプロセスのCSセグメントをeaxレジスターに取得します。CSセグメントの特権情報をチェックすることで、ユーザモードで割り込まれたか、カーネルモードで割り込まれたかを判断します。カーネルモードで割り込まれたなら、resume_kernelへジャンプします。ユーザモードならresume_userspaceからrestore_allへとユーザモードに復帰することになります。
ret_from_intr:
       GET_THREAD_INFO(%ebp)
check_userspace:
       movl PT_EFLAGS(%esp), %eax      # mix EFLAGS and CS
       movb PT_CS(%esp), %al
       andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
       cmpl $USER_RPL, %eax
       jb resume_kernel                # not returning to v8086 or userspace

ENTRY(resume_userspace)
       LOCKDEP_SYS_EXIT
       DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                                       # setting need_resched or sigpending
                                       # between sampling and the iret
       TRACE_IRQS_OFF
       movl TI_flags(%ebp), %ecx
       andl $_TIF_WORK_MASK, %ecx      # is there any work to be done on
                                       # int/exception return?
       jne work_pending
       jmp restore_all
resume_kernelでカーネルモードで割り込まれた時の処理で、プリエンプションにかかる処理となります。まず割り込まれたプロセスのthread_info構造体のメンバーpreempt_countをチェックします。thread_info構造体は、プロセス毎のカーネルスタックの延長上に有していて、ebpから取得できます。

preempt_countが0でなければ、プリエンプションは禁止です。restore_allで、プロセスの割り込まれたカーネルパスに復帰します。preempt_countが0で、thread_info構造体のflagsがNEED_RESCHEDなら、preempt_schedule_irqをコールすることで他のプロセスにCPUを手放します。

なお、割り込まれたプロセスの割り込みが、禁止されていたなら(コメントにあるように例外処理)、割り込まれたプロセスに復帰するようです。
ENTRY(resume_kernel)
       DISABLE_INTERRUPTS(CLBR_ANY)
       cmpl $0,TI_preempt_count(%ebp)  # non-zero preempt_count ?
       jnz restore_all
need_resched:
       movl TI_flags(%ebp), %ecx       # need_resched set ?
       testb $_TIF_NEED_RESCHED, %cl
       jz restore_all
       testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)    # interrupts off (exception path) ?
       jz restore_all
       call preempt_schedule_irq
       jmp need_resched
END(resume_kernel)

補足

この辺の詳細(割り込みの禁止/許可等の兼ね合い)は、SMPとの兼ね合いで、今ひとつ分かりません。


最終更新 2012/03/22 16:06:38 - north
(2012/03/22 15:48:53 作成)


検索

アクセス数
3689322
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。