More Related Content Similar to 4章 Linuxカーネル - 割り込み・例外 4 (20) 4章 Linuxカーネル - 割り込み・例外 41. 4 章 Linux カーネル – 割り込み・例外 4
・例外ベクタと例外ハンドラ
・割り込みディスクリプタテーブル
・割り込み信号発生からハンドラ実行まで
・ハンドラ実行終了後の処理
・割り込み / 例外処理のネスト
mao
Web >https://www.pridact.com
Twitter >https://twitter.com/rivarten
Mail >official@pridact.com
4. カーネル参考文献
< 参考書 >
〜基本的に古い情報だが、基本は学べる〜
・詳解 LINUX カーネル 第 3 版 (O’REILLY
・ Linux カーネル 2.6 解読室 (Softbank Creative
・ Linux カーネル解析入門 ( 工学社
・ Modern Operating Systems 3e International
(Pearson, Andrew S. Tanenbaum)
〜初期化部分を知りたいなら〜
・新装改訂版 Linux のブートプロセスを見る
( アスキー・メディアワークス )
< 参考 Web>
・ Linux Cross Reference[http://lxr.free-electrons.com]
・ Wikipedia
5. 例外ベクタと例外ハンドラ (1)
・例外ベクタ (x86)
ベクタ番号 名前 種類 説明
0 除算エラー (Devide Error) フォルト プログラムが 0 で整数除算すると発生
1
デバッグ例外 (Debug) トラップ ,
フォルト
eflags レジスタの TF=1 で発生 ( デバッガでの
ステップ実行に有用 ). 有効化したデバッグレ
ジスタのアドレス範囲内にある命令を実行し
たり , オペランドへアクセスしたりした時もデ
バッグ例外が発生 .
2
マスク不可割り込み (Non-Maskable
Interrupt) ---
マスク不可割り込み用に予約
(NMI ピンを使用 )
3
ブレークポイント (Breakpoint) トラップ int3( ブレークポイント ) 命令によって発生 .
通常はデバッガがこの命令を埋め込む .
4
オーバーフロー (Overflow) トラップ into( オーバーフローを調べる ) 命令を実行し
た時に ,eflags レジスタの OF(overflow) レジス
タ フラグが設定されている場合に発生
5
Bound 範囲超過 (Bounds check) フォルト bound( アドレス範囲を調べる ) 命令を , アドレ
スの有効範囲外にあるオペランドとともに実
行すると発生 .
6
無効オペコード (Invalid opcode) フォルト CPU が無効なオペコード ( 実行する動作を決
定するマシン語の一部 ) を検出すると発生
7
デバイス使用不可能 (Device not
available)
フォル ESCAPE 命令 ,MMX 命令 ,SSE/SSE2 命令を
実行した時に ,cr0 レジスタの TS フラグが設
定されている場合に発生
6. 例外ベクタと例外ハンドラ (2)
・例外ベクタ (x86)
ベクタ番号 名前 種類 説明
8
ダブルフォルト (Double falult) アボート CPU が例外ハンドラを呼び出す時に別の例
外を検出すると , 通常 2 つの例外は順次処
理されるが , 稀にプロセッサが連続して処理
できない場合があり , その時に発生 .
9
コプロセッサセグメントオーバーラン
(Coprocessor segment overrun)
アボート 外部算術演算コプロセッサで問題が起こると
発生 . 古い 80386 プロセッサの場合にのみ発
生
10
無効 TSS(Invalid TSS) フォルト 無効な TSS を持つプロセスに切り替えようと
した場合に発生
11
セグメント不在 (Segment not present) フォルト メモリ中に存在しない ( セグメントディスクリプ
タの P フラグが設定されていない ) セグメント
を参照した場合に発生
12
スタックセグメントフォルト (Stack
segment fault)
フォルト 命令がスタックセグメントの範囲を越えようと
した場合 , または ss レジスタが指すセグメン
トがメモリ中に存在しない場合に発生 .
13
一般保護 (General protection) フォルト x86 のプロテクトモードの保護規則に違反し
た場合に発生
14
ページフォルト (Page fault) フォルト 参照されたページがメモリ上に存在しない
か , 対応するページテーブルエントリが NULL
か , ページング保護機構に違反した場合に発
生
7. 例外ベクタと例外ハンドラ (3)
・例外ベクタ (x86)
ベクタ番号 名前 種類 説明
15 Intel が予約済み ---
16
浮動小数点エラー (Floating point error) フォルト CPU に組み込まれている浮動小数点回路が
エラーを検出した場合に発生 . 算術オーバー
フローやゼロ除算など .x86 では , 符号付き除
算の結果が符号付き整数にならない場合 (-
2,147,483,648/-1 など ) にも発生 .
17
アラインメントチェック (Alignment check) フォルト オペランドのアドレスが正しくアラインされて
いない場合に発生
18
マシンチェック アボート マシンチェック機構が CPU やバスのエラーを
検出した場合に発生 .
19
SIMD 浮動小数点例外 (SIMD floating
point exception)
フォルト CPU に組み込まれている SSE や SSE2 回路
が浮動小数点演算でエラーを検出した場合
に発生 .
20~31
将来のために Intel が予約
---
8. 例外ベクタと例外ハンドラ (4)
・例外ハンドラ (x86)
ベクタ番号 例外 ハンドラ シグナル
0 除算エラー (Divide Error) divide_error() SIGFPE
1 デバッグ例外 (Debug) debug() SIGTRAP
2
マスク不可割り込み (Non-Maskable
Interrupt)
nmi()
---
3 ブレークポイント (Breakpoint) int3() SIGTRAP
4 オーバーフロー (Overflow) overflow() SIGSEGV
5 Bound 範囲超過 (Bounds check) bounds() SIGSEGV
6 無効オペコード (Invalid opcode) invalid_op() SIGILL
7
デバイス使用不可能 (Device not
available)
device_not_available()
---
8 ダブルフォルト (Double falult) doublefault_fn() ---
9
コプロセッサセグメントオーバーラン
(Coprocessor segment overrun)
coprocessor_segment_ove
rrun()
SIGFPE
10 無効 TSS(Invalid TSS) invalid_TSS() SIGSEGV
11 セグメント不在 (Segment not present) segment_not_present() SIGBUS
12
スタックセグメントフォルト (Stack
segment fault)
stack_segment() SIGBUS
9. 例外ベクタと例外ハンドラ (5)
・例外ハンドラ (x86)
ベクタ番号 例外 ハンドラ シグナル
13 一般保護 (General protection) general_protection() SIGSEGV
14 ページフォルト (Page fault) page_fault() SIGSEGV
15 Intel が予約済み --- ---
16 浮動小数点エラー (Floating point error) coprocessor_error() SIGFPE
17 アラインメントチェック (Alignment check) alignment_check() SIGBUS
18 マシンチェック (Machine check) machine_check() ---
19
SIMD 浮動小数点例外 (SIMD floating
point exception)
simd_coprocessor_error() SIGFPE
10. 割り込みディスクリプタテーブル (1)
・割り込みディスクリプタテーブル IDT
(Interrupt Descriptor Table)
・割り込みや例外のベクタとハンドラのアドレスの対応を保持 .
・形式は GDT や LDT とほぼ同じ .
・ IDT 最大サイズは 256×8byte=2KB
・ IDT はメモリ上の任意の位置に置くことができる .
・ CPU の idtr レジスタに IDT のベースリニアアドレスとリミットを指定 .
・割り込みを使用する前に lidt アセンブリ命令で初期化する .
・ 40~43bit(type) の内容がディスクリプタの種類を表している .
・ディスクリプタの種類
・タスクゲート
・割り込みゲート
・トラップゲート
Linux では , 割り込み処理に割り込みゲートを使用し ,
例外処理にトラップゲートを使用している .
14. 割り込み信号発生からハンドラ実行まで (1)
・前提条件
・カーネルの初期化が完了し ,CPU がプロテクトモードで動作している .
0. ある 1 つの命令を実行後 ,cs/eip レジスタは次に実行すべき命令のアドレ
スが入っている . これを実行する前に , 制御回路は前の命令の実行中に
割り込み・例外が発生していないかを調べる (INTR,NMI を調べる )
発生していれば , 制御回路は以降の処理を進める .
1. 割り込み・例外に対応するベクタ (0≦i≦255) を取得する .
2.idtr レジスタが指す IDT から i 番目のエントリを読み取る .
( 該当エントリには割り込みゲートかトラップゲートが存在する )
3.gdtr レジスタから GDT ベースアドレスを取得し ,
GDT 内から IDT エントリのセレクタがさすセグメントディスクリプタを取得 .
このディスクリプタに , 割り込み / 例外ハンドラがあるセグメントのベースア
ドレスがある .
15. 割り込み信号発生からハンドラ実行まで (2)
4. 割り込み元の正当性の確認 .
・ cs レジスタの CPL と GDT 内のセグメントディスクリプタの DPL を比較 .
CPL: 割り込みを発生させたプログラムの現行特権レベル
DPL: セグメントアクセスに必要な特権レベル
・ DPL の特権レベルより CPL の特権レベルの方が低い
( 値としては ,CPL>DPL)
⇒一般保護例外 .
割り込みハンドラは , 割り込みを発生させたプログラムより
低い特権レベルでは動作できない .
・ OK
ソフトウェア的に生成された割り込み / 例外の場合 (int n,int 3,into
命令等 ), さらなるセキュリティチェックを行う .
・ CPL と IDT 内のセグメントディスクリプタの DPL を比較
・ DPL の特権レベルより CPL の特権レベルの方が低い
( 値としては ,CPL>DPL)
⇒一般保護例外 .
ユーザアプリケーションによるトラップゲートや割り込み
ゲートへのアクセスを防ぐ .
16. 割り込み信号発生からハンドラ実行まで (3)
5. 特権レベルが変更されているかを調べる .
CPL ≠ セグメントディスクリプタの DPL
⇒ 制御回路は新しい特権レベル用のスタックを用意する .
a. tr レジスタを読み取り , 実行中のプロセスの TSS にアクセス .
b. ss/esp レジスタに ,TSS に置かれている新しい特権レベル用の
論理アドレスを ss/esp に読み込む .
c. 古い特権レベル用スタックの論理アドレスを表す ss/esp を ,
新しいスタックに退避 .
6. フォルトが発生した場合 , 例外を発生させた命令の論理アドレスを
cs/eip に読み込む . これで再実行が可能になる .
7. スタックに eflags,cs,eip レジスタの内容を退避する .
8. 例外がハードウェアエラーコードを持っていれば , スタックに退避する .
9.IDT 内の i 番目のエントリにあるセグメントセレクタとゲートディスクリプタの
オフセットフィールドを ,cs/eip レジスタに読み込む .
これが割り込み・例外ハンドラの最初の命令の論理アドレスを表す .
push 順 :[ss,esp] → [eflags,cs,eip] → ( ハードウェアエラーコード )
17. ハンドラ実行終了後の処理
iret 命令を実行して , 割り込まれたプロセスに制御を移す .
pop 順 :[ss,esp] ← [eflags,cs,eip] ← ( ハードウェアエラーコード )
1. スタックに退避されている cs,eip,eflags レジスタの値を読み込む .
2. ハンドラの CPL が ,cs の CPL と同じかどうか調べる .
( 割り込まれたプロセスがハンドラと同じ特権レベルで動作していたか )
・同じ⇒ iret は実行を終了
・違う⇒次のステップに進む .
3. スタックから ss/esp レジスタを読み込む .
( 古い特権レベル用のスタックに戻る )
4.ds,es,fs,gs セグメントレジスタを調べる .
CPL の特権レベル > DPL の特権レベル ( 値は ,CPL<DPL) となるセグメン
トディスクリプタを参照するセレクタがあれば ,
そのセグメントレジスタをクリア .
カーネルルーチンが実行したセグメントを , それより特権レベルが低いプロセスが使用してしまう事を防ぐ .
クリアしなければ , カーネル空間にアクセス可能になってしまう .
18. 割り込み・例外処理のネスト (1)
・割り込み / 例外のカーネル実行パスがネストすることもある .
・カーネル実行パスのネストを許可する条件
「割り込みハンドラ実行中に絶対にプロセスを切り替えないこと」
カーネル実行パスを再開させる為に必要なデータは ,
カレントプロセスのカーネルスタックに積まれる為 ,
割り込みハンドラ動作中に , 他のプロセスに切り替えられない .
・例外のほとんどはユーザーモードの時だけ発生 .
ページフォルト例外はカーネルモードでも発生 .
システムコールのカーネル実行パス + ページフォルト例外のカーネル実行パス
・割り込みは , カレントプロセスのデータを参照しない .
・割り込みハンドラは , 別の割り込みハンドラや例外ハンドラに
割り込むことがある .
・例外ハンドラが割り込みハンドラに割り込むことはない .
・マルチプロセッサにおいて , 例外用のカーネル実行パスは ,
ある CPU で処理開始された後 , プロセスの切り替えによって ,
別の CPU に移動することもある .
19. 割り込み・例外処理のネスト (2)
・ Linux がカーネル実行パスのネストを許可する理由
1.PIC やデバイスコントローラのスループット向上 .
PIC やデバイスコントローラは ,
CPU から ACK を受取るまで処理を止めたまま .
カーネル実行パスを切り替えて動作させると , 他の割り込み処理の
実行中であっても , カーネルが ACK を返すことができる .
2. 優先度レベルがない割り込みモデルを実現できる .
割り込みハンドラは , 他の割り込みハンドラの処理を遅延できる為 ,
デバイスに優先順位をあらかじめ与えておく必要がない .
これにより , カーネルコードが単純化でき , 移植性が向上 .