割り込み処理について
初めに
今回の記事も前回同様、技術的な内容ではなく私が理解を深めるためのメモであり
特に面白みのないものとなっています。
また、誤字脱字、間違っている点も多くあると思われますのでご了承ください。
割り込み
- 割り込みとは、ハードウェアから特定の処理を要求されたときに、一時的にソフトウェアの処理を中断させて要求された処理を実行することをいう。
例)キーボードコントローラ->キーが押されたときにCPU割り混みを信号を出し、CPUはどのキーが押されたのかをキーボードコントローラから読み出す処理を行う 割り込みを行うことでCPUがハードウェアを常に監視する必要性がなくなる
割り込みの種類
- 例外(Exception)
- 割り込み要求(IRQ: Interrupt ReQuest)
- ソフトウェア割り込み
例外(Exception)
- CPUが異常を検出した際に発生する。
- ページングで割り当てられたページがない時などに異常を検出して例外処理を行う。
- 主にソフトウェアでの処理で異常を検出する
例) 存在しない配列要素へのアクセス
割り込み要求
- CPUの周辺のハードウェアから要求があった場合に発生する信号
- CPUのピンとハードウェアは電気的にワイヤで接続されており、ハードウェアは割り込み要求を出すときにそのワイヤへ電気信号を出すことでCPUに伝達する
- CPUはワイヤの信号が変化したことを検出すると要求にあった割り込みハンドラを呼び出す。
- この信号はソフトウェアの動作に関係なくいつ信号が送信されるかはわからない
- CPUはPIC(Programmable Interrupt Controller)とわいやでIRQラインが結ばれている。
- PICはフロッピーディスクコントローラやDMAコントローラなどの割り込み要求を一括してCPUに伝える。
ソフトウェア割り込み
- 例外とは異なり、意図的にソフトウェアが発生させることができる割り込み。
- INT命令がソフトウェア割り込みでを発生させるめいれいであり、主にユーザプログラムがシステムコールを呼び出すときに使用する。
割り込みベクタと割り込みハンドラ
- 割り込みが発生したときにどのハンドラを呼び出すのかを記述したものを割り込みハンドラという。
- 割り込みベクタには割り込みハンドラのアドレスが順番に並んでいるようなテーブルのイメージで番号がつけられている。
- 割り込みが発生したときに、その割り込みに対応した番号のハンドラは呼び出されることで、割り込み処理が行われる。
- 割り込みベクタのうち、例外の部分はCPU仕様によって決まっている。
GDT(Global Descriptor Table)
- 64bitを一つのデータとした配列
- 一つの要素である64bitので0他派セグメントディスクリプタといい、セグメントの開始位置、セグメントの崔伊豆アクセス権限などを細かく設定できる。
- このセグメントディスクリプタは最大で8192個まで持つことができる。
IDT(Interrupt Descriptor Table)
- 割り込みと割り込みハンドラを結びつけるテーブルで、8byteのディスクリプタ。
- IDTは256個までの配列でそれぞれ順番に0番から255番までの割り込みに対応している。
GDTと似たディスクリプタを構成していて、3種類のディスクリプタがある。
-> タスクゲートディスクリプタ
-> 割り込みゲートディスクリプタ
-> トラップゲートディスクリプタタスクゲートディスクリプタを使用して割り込みハンドラ処理を行うとタスクスイッチができる。
- 割り込み/トラップディスクリプタは割り込み/例外時に使用するディスクリプタ
- 割り込みゲートディスクリプタとトラップゲートディスクリプタはほとんど同じ動作をするが、割り込み/例外発生時に、割り込みゲートディスクリプタはEFLAGSレジスタのIF(割り込みフラグ)をクリアし、割り込み処理中は他の割り込みが入らないようにする一方、トラップゲートディスクリプタはEFLAGSフラグを変更しないため、他の割り込み/例外を許可する。
IDTR
- CPUが持っている特殊なレジスタを指す。
- LIDT命令を使用してIDTRにIDTのサイズとアドレスを格納する。
- IDTRにIDTのサイズとアドレスが格納されている際にわりこみが発生することでCPUはIDTを参照し、対応する割り込みハンドラの処理を行う。
LDT(Local Descriptor Table)
- タスク毎にGDTの相当するディスクリプタを持たせることでタスク間のセキュリティを高める仕組み
- すべてのタスクはGDTをルートディスクリプタとして共通に使う
- GDT内のあるエントリを、LDTディスクリプタと割り当て、タスクスイッチング毎にそのディスクリプタを更新することで、タスク毎のLDTを実装する。
- セグメントは16bit、下位の3bitは属性、上位はセレクタ値
- セグメントがLDTを示す場合、IDTRレジスタがGDT内のLDTディスクリプタとし、そこからLDTを取得しLDT内のエントリからディスクリプタテーブルを取得する。
- タスク切り替え毎にIDTRレジスタとGDT内のエントリを切り替える必要がある。