気になったことのあれこれ。この広告は、90日以上更新していないブログに表示しています。2.6.11のコードリーディングをしていく。CPUのアーキテクチャは書籍に沿ってIntelのx86とする。今回は例外の種類及び割り込みディスクリプタの構造などについて見ていく。例外x86では約20種類の例外を発生させる。0で整数除算すると発生する。トラップ又はフォルト。eflagレジスタのTFフラグを設定すると発生する。マスク不可割り込み用に予約。int3命令(ブレークポイント)によって発生する。通常はデバッガが埋め込む。into命令(オーバーフローを検出する)を実行した時にeflagレジスタのOF(overflow)フラグが設定されている場合に発生する。bound命令(アドレス範囲を調べる)をアドレスの有効範囲外にあるオペランドと共に実行すると発生する。CPUが無効なオペコードを検出する。フォルトESCAPE命令やMMX命令、又はSSE/SSE2命令を実行した時にcr0レジスタのTSフラグが設定されている場合に発生する。CPUが例外ハンドラを呼び出す時に別の例外を検出すると通常は順次処理されるが、何らかの理由により処理できない場合に呼び出される。外部算術演算コアプロセッサで問題が起こった場合に発生する。無効なタスク状態セグメント(TSS)を持つプロセスに切り替えようとした場合に発生する。メモリ中に存在しないセグメントを参照した場合に発生する。命令がスタックセグメントの範囲を越えようとした場合、又はSSレジスタがさすセグメントがメモリに存在しない場合に発生する。x86のプロテクトモードの保護規則に違反した場合に発生する。参照されたページがメモリに存在しない場合、もしくは対応sルウページテーブルのエントリがNULLか、又はページング保護機構に違反した場合に発生する。Interlが予約している。CPUに組み込まれている浮動小数点回路がエラーを検出した場合に発生。算術オーパフローやゼロ除算など。オペランドのアドレスが正しくアラインされていない場合に発生する。マシンチェック機構がCPUやバスのエラーを検出した場合に発生する。CPUに組み込まれているSSEやSSE2回路が浮動小数点演算でエラーを検出した場合に発生する。31の値はIntelが将来のために予約している。Table)というテーブルは割り込みや例外のベクタとハンドラのアドレスの対応表である。Linuxでは例外に32エントリ、割り込みに224エントリあてられる。Register)が保持している。Table)アセンブリ命令で初期化する必要がある。IDTは以下のように定義されている。};ディスクリプタには以下の3種類が存在する。Gate)プロセスのTSSを配置する。割り込みが発生した際に現在実行しているプロセスのTSSとリプレイスする。Linuxはタスクゲートを使用しない。Gate)割り込みまたは例外ハンドラの存在するセグメントの、セレクタやオフセットを配置する。当該セグメントに制御を移す間は、プロセッサはEFLAGレジスタのIFフラグを落としマスク可能割り込みを禁止する。当該ゲートがコールされた時に呼び出されるカーネル関数へのアドレスのオフセット(16bit目~31bit目)トラップゲート基本的にCPUからの例外をハンドルするために使用される。割り込みゲートとは異なりIFフラグをクリアしない。当該ゲートがコールされた時に呼び出されるカーネル関数へのアドレスのオフセット(16bit目~31bit目)基本的にLinuxでは割り込み処理に割り込みゲート、例外処理にトラップゲートを使用している。ハードウェア割り込みと例外の処理カーネルの初期化が完了しておりCPUがプロテクトモードで動作していると仮定する。csとeipが次に実行する命令の論理アドレスを保持している。次に実行対象である命令を実行する前にCPUは割り込み又は例外が発生していないかを確認し発生していれば以下の処理を行う。(IDTに格納されているのはトラップゲート又は割り込みゲートであることを前提とする)割り込み又は例外に対応するベクタを取得。vector))を取得。gdtrレジスタからGDTのベースアドレスを取得し、GDT内から先ほど取得したIDTエントリが指すセグメントディスクリプタを取得。当該ディスクリプタが割り込み又は例外ハンドラが存在するセグメントのベースアドレスを指す。Level)と比較する。DPLの場合には一般保護例外を発生させる。CPLの場合一般保護例外を発生させる。制御回路は新しい特権レベル用のスタックを使用する必要がため、CPLとDPLを比較し特権レベルの変更の有無を確認する。Segment)にアクセス。ssとespに新しい特権レベル用のスタックセグメントとスタックポインタを設定。古い特権レベル用のスタックの論理アドレスを保持するssとespを新しいスタックに退避する。フォルトが発生した場合は例外を発生させた命令の論理アドレスをcsとeipに読み込む。Faultなど)スタックにeflag、cs、eipレジスタの内容を退避する。例外がハードウェアエラーコードを保持していれば、当該値をスタックに退避する。取得したIDTエントリが保持するセグメントセレクタとゲートディスクリプタのオフセットフィールドを、それぞれcsとeipに読み込む。割り込み又は例外処理の完了後はiret命令を使用し、以下の流れで割り込んだプロセスに制御を戻す。スタックに退避されているcs、eip、eflagsレジスタの値を読み込む。スタックにエラーコードが退避されている場合は取り除く。ハンドラのCPLがcsレジスタの下位2bitを等しいかをチェックし、等しければiret命令は実行を終了する。異なる場合は以下の処理に続く。スタックからssとespを読み込むことで古い特権レベルのスタックに戻る。ds、es、fs、gs、セグメントレジスタをチェックし、CPLの値よりも小さいDPLを保持するディスクリプタを参照するセグメントセレクタをクリアする。例外及び割り込みのネスト割り込みが発生した際に最初に実行されるのはCPUレジスタの内容をカーネルスタックに退避すること、そして最後に実行されるのはそれらをリストアすることである。割り込みは入れ子状態になることもあり、割り込みを抜けた先が割り込みであるといったことが起こる。加えて割り込みの際には退避される情報がカーネルスタックに積まれるためカレントプロセスと密接に結びついている。大部分の例外はCPUがユーザモードの時に発生するが「ページフォルト」はカーネルモードの場合でも発生する。当該例外はアクセスしたアドレス空間が物理RAMにマッピングされていない場合に発生する。当該例外を処理している間カーネルは他のプロセスを実行することもある。「ページフォルト」ハンドラはさらなる例外を発生させないので。例外によるネストは最大でも2つとなる。(1つ目はシステムコール、2つ目がページフォルト)I/Oデバイスが発生させる割り込みはカレントプロセスのデータを参照しないような処理のため、どのプロセスで実行されるかがわからない。割り込みハンドラが別の割り込みハンドラに割り込むことはあるが、逆に例外ハンドラが割り込みハンドラに割り込むことはない。カーネルモードでの唯一の例外は「ページフォルト」であり、割り込みハンドラ内には当該例外を起こすような処理な行わないからだ。よってプロセスが切り替わることもない。カーネルが割り込みのネストを許可するのには以下のような理由がある。PICやデバイスドライバのスループットを向上させる目的。デバイスコントローラがIRQラインに信号を発した際、PICが信号を外部割り込みに変換しCPUに送信する。PICがCPUからのACKを受け取るまではPICもデバイスコントローラも処理を停止させた状態となる。他の割り込みの最中にカーネルが他の割り込みを受け付けることで、すぐにACKを返すことができる。優先度レベルのない割り込みモデルを実現できる。どの割り込みハンドラの処理でも遅延可能で、ハードウェアデバイスに優先度を設定しておく必要がないため、カーネルを単純化し移植性を高めることができる。マルチコアプロセッサでは複数のプロセスが走るため、プロセスの切り替えによって別のCPUに移動することがある。割り込みディスクリプタテーブルの初期化カーネルが割り込みを許可する前に以下の2点を行っておく必要がある。idtrレジスタにIDTの先頭アドレスを設定IDT内の全エントリの初期化ユーザプロセスはint命令にベクタ番号を指定して割り込み信号を発生させることができるので、IDTの初期化時に特定の割り込みやトラップのディスクリプタのDPLを0に設定しておく必要がある。数は少ないがユーザプロセスが直接生成できるものもあるため、対象のディスクリプタのDPLのみ3を設定する。割り込みゲートIntelの割り込みゲートでユーザプロセスからはアクセスできないものとなっている(DPL=0)。Linuxの割り込みハンドラは全て割り込みゲートで実装されている。割り込みゲートを登録する関数であるset_intr_gate()の定義を以下に示す。内部で呼ばれている_set_gate()を見ると、第一引数はIDTの何番目のエントリか、第二引数はエントリタイプ、第三引数はDPL、第四引数は関数のアドレス、第五引数はセグメントの指定となっているのがわかる。_set_gate(idt_table+n,14,0,addr,__KERNEL_CS);}システムゲートIntelの言うトラップゲートでユーザプロセスからもアクセスが可能(DPL=3)。0x80の3つの命令はユーザプロセスも使用が可能となる。システムゲートを登録する関数であるset_system_gate()の定義を以下に示す。_set_gate(idt_table+n,15,3,addr,__KERNEL_CS);}システム割り込みゲートIntelで言う割り込みゲートでユーザプロセスからもアクセスが可能(DPL=3)。ベクタ番号3のハンドラは当該ゲートで実装されているため、int3命令はユーザプロセスからも利用可能。__KERNEL_CS);}トラップゲートIntelのいうトラップゲートでユーザプロセスがアクセス可能でないもの(DPL=0)。Linuxではほとんどの例外ハンドラをこのトラップゲートで実装している。_set_gate(idt_table+n,15,0,addr,__KERNEL_CS);}タスクゲートIntelの言うタスクゲートでユーザプロセスからはアクセス不可能(DPL=0)。Linuxではダブルフォルト例外ハンドラは当該ゲートで実装されている。_set_gate(idt_table+n,5,0,0,(gdt_entry<<3));}上記の関数内部で呼ばれている_set_gate()の定義は以下。(0)上記では各引数の値をeax及びedxにセットし、それぞれ32bitずつのデータをゲートディスクリプタのアドレスを起点に+0及び+1の場所に保存している。xv6のコードリーティングを通してUnixの動作を追う。»Profile低レイヤ好きのソフトウェアエンジニア。(1)はてなブログをはじめよう!k--onishiさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。