割り込み後、プロセッサはどのようにカーネルコードを検出しますか?


13

割り込みが発生すると、プロセッサは現在のプロセスを横取りし、カーネルコードを呼び出して割り込みを処理します。プロセッサは、カーネルに入る場所をどのようにして知るのですか?

各割り込みラインにインストールできる割り込みハンドラがあることを理解しています。しかし、プロセッサーは「ハードワイヤードロジック」のみを実行するため、割り込みハンドラー自体、またはハンドラーの前に実行されるコードのいずれかを指す定義済みの場所が存在する必要があります(1つの割り込み行に複数のハンドラーがあるため、後者)。

回答:


13

カーネルは、起動時に、各行の割り込みハンドラーを指す割り込みベクターテーブル(x86では割り込み記述子テーブルまたはIDT と呼ばれます)を初期化します。

80286より前は、IDTは常に固定アドレスに保存されていました。80286以降、IDTはLIDT命令を使用してロードされます。

割り込みベクターテーブルは、割り込み行ごとに1つのハンドラーを指します。そうは言っても、カーネルは、たとえば、他のいくつかの割り込みルーチンを実行する割り込みハンドラーを提供するか、一部またはすべての割り込みをカバーする単一のハンドラーを提供するかを選択できます。Linuxは、どの割り込みラインが呼び出されたかを判断し、呼び出す適切なダウンストリームハンドラーを見つける汎用割り込みハンドラーを提供することにより、これらのことを行います。


1
プロセッサはIDTのインデックスとして割り込みラインを使用し、PCにエントリを配置して実行を開始しますか?しかし、すべての割り込みハンドラーの前に実行される汎用関数はありませんか?Linuxの場合、do_IRQ()になります。これは、割り込みラインに関係なく、すべてのIDTエントリが指す関数ですか?
フィリップマリー14

@PhilippMurryはい。カーネルは、以前に実行したコードに戻る前に、独自の割り込みハンドラーのセット(1行に複数ある可能性がある)を使用して実際に割り込みを処理します。
アダムマラス14

さて、実際には2種類の割り込みハンドラーがあります。プロセッサーが呼び出すもの(常にdo_IRQ())と、カーネルが呼び出すもの(request_irq()で登録したもの)です。これを答えに追加していただけますか?私はそれを受け入れると思う:)どうもありがとう
フィリップマリー14

1
@PhilippMurry私はLinuxが割り込みを処理する方法(およびカーネル開発者がそのシステムを利用する方法)の専門家ではありませんが、カーネルが独自のISR管理を持つ方法について、より広い意味でいくつかの追加情報を追加しました。
アダムマラス14

12

はい、ジャンプするコードのアドレスを含む定義済みの場所があります:割り込みベクター。プロセッサに応じて、これは物理メモリの特定の場所(8088)、仮想メモリの特定の場所、プロセッサレジスタ、レジスタによって示されるメモリの場所(ARM、386)などです。

詳細はプロセッサによって異なりますが、プロセッサで割り込みを処理するための主な共通要素は次のとおりです。

  • 割り込みをマスクします(したがって、後続の割り込みは待機する必要があります)。
  • プロセッサモードをカーネルモードまたは割り込みモードに設定します(プロセッサにそのようなモードがある場合)。
  • プログラムカウンターの値を既知の場所(レジスタまたはメモリ)に保存します。
  • 他のレジスタの値を保存するか、レジスタバンクを切り替えることができます。
  • 次の命令を(新しいPC値で)実行します。

1

他の2つの回答(執筆時点)では、割り込みとIDTについて説明しています。これは正しいですが、最新のIntel風のCPUでは、カーネルを呼び出す方法は3つ以上あります。

方法#1:割り込み。

これは上で説明されています。割り込み記述子テーブル/割り込みベクトルにエントリを設定し、ソフトウェア割り込みを実行してカーネルに入ります。

この方法の主な利点は、典型的なカーネルがとにかく割り込みを処理できる必要があり、それが古風なハードウェアで動作することです。

方法#2:ゲートを呼び出します。

コールゲートは、特別な種類のセグメントセレクタです。呼び出しのターゲットは、グローバルまたはローカルセグメント記述子テーブル(それぞれGDTおよびLDT)にロードする必要があります。次に、コールゲートをセグメントとして使用してファーコール命令を実行すると(コールのオフセットは無視されます)、これにより、より多くの特権コードを呼び出すことができます。コールゲートは非常に柔軟です。IA-32アーキテクチャには4つの特権レベルがあり、コールゲートを使用して任意のレベルを呼び出すことができます。

Linuxがコールゲートを使用したことはないと思いますが、Windows 95は使用しました。Win95カーネルサービス(krnl386.exeおよびkernel.dll)は、実際にはユーザーモード(リング3)で実行されました。最高の特権レベル(リング0)は、プロセスの切り替えのみを実行するドライバーとマイクロカーネルにのみ使用されました。ドライバーへの呼び出しは、コールゲートを使用して行われました。これにより、従来の16ビットコード(その多くがありました!)が、いつものように、標準のfar呼び出しを使用してWin95ドライバーを使用できるようになりました。

グローバル記述子テーブルの不適切な保護は、いくつかのWindows 95の悪用の原因であり、メモリを上書きすることで独自のコールゲートをインストールすることができました。

方法#3:SYSCALL / SYSRETおよびSYSENTER / SYSEXIT

これらは、AMDとIntelが独自に発明した2組の命令ですが、基本的に同じことを行います。SYSCALL / SYSRETが最初に登場し、AMDのみでした。SYSENTER/ SYSEXITはIntelでしたが、AMDは現在実装しています。そこで、SYSENTER / SYSEXITについて説明します。

コールゲートとは異なり、SYSENTERはリング0への転送にのみ使用でき、1つの場所にのみ転送できます。ただし、呼び出しや割り込みとは異なり、スタックに触れないため、レイテンシが非常に低いという利点があります。

転送場所は、3つのモデル固有のレジスタを使用して設定されます。1つはセグメント情報用で、もう1つはカーネルコードの命令ポインタとスタックポインタ用です。スタックには何も「プッシュ」されないため、ユーザーモードコードは、レジスタにリターン命令ポインターとスタックポインターを渡すことで、カーネルにどこに戻るかを指示します。カーネルはスタックポインターを復元し、SYSEXIT命令は命令ポインターを復元します。

SYSENTERおよびSYSEXIT命令の詳細。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.