Linuxシステムでページ障害が発生した場合、割り込みハンドラはページ障害が発生した理由を見つける必要があります。しかし、どのように?
- 特別な番号がありますか?それでは、この数字はどこに記録されていますか?
- 例外を発生させる前にページ障害の原因を知ることができますか?
例えば。- ステップ1
CPUによる原因の特定 - ステップ2
例外発生
- ステップ1
答え1
仮想アドレスに対するMMUルックアップが無効な記述子または権限不足を示す記述子(たとえば、読み取り専用ページに書き込もうとするなど)で終わるため、メモリアクセスに失敗するとページエラーが発生します。ページエラーが発生すると、プロセッサは複数のタスクを実行します。詳細は各プロセッサアーキテクチャによって異なりますが、ポイントは同じです。
- 特権モード(カーネルモードなど)に切り替えます。
- 少なくともエラーの性質とエラーポイントのプログラムカウンタとプロセッサモードを示すようにいくつかのレジスタを設定します。
- レジスタによって表示されるか、メモリ内の特定の場所(ページフォルトハンドラのアドレス)から検索されたメモリの特定のアドレスにジャンプします。
たとえば、(32ビット)ARMプロセッサの場合:
- これ
dfsr
レジスタは、エラー(読み取りまたは書き込み、プロセッサ命令、DMAなどによるものかどうか)を記述する値に設定されます。 - これ
dfar
レジスタは、エラーが発生したアクセス先の仮想アドレスに設定されます。 - プロセッサが次に切り替わります。中断モード(カーネルレベルの権限の一つモデル)。
- エラーが発生すると、
lr
このレジスタはプログラムカウンタに設定され、エラーが発生するとこのレジスタspsr
はプログラムステータスレジスタ(cpsr
モードビットなどを含むレジスタ)に設定されます。 sp
レジスタはcpsr
保存されます。中断モードで最後に設定された値から復元されます。- 実行は中断ベクトルにジャンプし、そのうちの1つは例外ベクトル。
ページエラーハンドラのコードはオペレーティングシステムカーネルの一部です。その使命は、失敗の原因を分析し、対策を講じることです。エラーの性質に関する情報を提供する特別なレジスタを参照することができ、必要に応じてプログラムが実行されている命令を確認できます。また、MMU テーブルに記述子を見つけることもできます。無効な記述子は、時々スワップ空間のページ位置などの情報をエンコードする可能性があります。カーネルは、すべてのコンテキスト遷移で更新されるグローバル変数またはレジスタの値を調べることで、現在どのタスクが実行されているかを知っています。以下は、ページエラーのいくつかの一般的な動作です。
- プロセスのメモリマップに関するデータは、ページがスワップ状態にあることを示します。カーネルは使用可能な物理ページを見つけ、ディスクキャッシュを含むページを削除するか、まずその内容をスワップ領域に保存して物理ページを取得します。その後、スワップのデータを物理ページにロードし、MMUテーブルを変更してエラーを引き起こした仮想アドレスが、プロセスのMMUマップ内の物理ページにリンクされます。最後に、カーネルはエラーを引き起こしたコマンドからプロセスに戻す準備をします。この時点で、コマンドは正常に実行されます。
- プロセスメモリマップのデータは、そのページが書き込み時にコピーページであり、書き込みアクセスが試みられたことを示します。以前のケースと同様に、カーネルは予備の物理ページを取得し、データをここにコピーし(ここでは読み取り専用ページで)、MMU記述子を変更してコマンドを再実行するようにプロセスをスケジュールします。
- プロセスメモリマップのデータは、ページがマップされていないか、必要な権限がないことを示します。この場合、カーネルはSIGSEGV信号(セグメントエラー)をプロセスに渡します。プロセスの実行は元の位置ではなく信号ハンドラで再開されますが、元の位置はスタックに保存されます。プロセスにSIGSEGVのハンドラがない場合は終了します。
メモリにアクセスする前に仮想メモリ構成を理解して確認しないと、例外がすぐに発生するかどうかを判断することができないことがよくあります。一般的な動作フローは、ページ障害が発生した場合にプロセッサがページ障害の原因を記録することです。