ページテーブル
仮想アドレスから物理アドレスへの変換は、カーネルが使用するメモリ内に保存されているページテーブルを用いる。
ページテーブル内に保存されている、ページテーブルエントリには各プロセスの仮想アドレスと物理アドレスの対応情報が記録されており、この対応から実際のメモリアクセスを行うことになる。
なお、ページのサイズは CPU アーキテクチャに依存し、x86_64 の場合は 4K バイトとなる。
仮想アドレス空間の大きさは固定であり、かつページテーブルエントリには、それぞれのページに関してそれらに紐づく物理メモリが存在するかを示すデータがある。
もし、物理メモリが紐づいていない仮想アドレスにアクセスした場合、ページフォルトという割り込みが CPU 上で発生し、カーネル内のページフォルトハンドラがトリガーされる。
ページフォルトハンドラは、プロセスによるそのメモリアクセスが不正なものではないかを検出し、もし不正なものである場合は SIGSEGV シグナルによりプロセスは強制終了される。
実際に不正アクセスしてみる
簡単なプログラムを作って実際に不正アクセスをしてみる。
コードは以下で、NULL Pointer Access するだけ。
segv.c
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = NULL;
puts("Before the invalid access");
*p = 0;
puts("After the invalid access");
exit(EXIT_SUCCESS);
}
実際に実行してみる。
$ ./segv
Before the invalid access
Segmentation fault (core dumped)
デバッグオプション付きでコンパイルしてみて、gdb で解析する。
$ gcc -o segv -g segv.c
$ ./segv
Before the invalid access
Segmentation fault (core dumped)
$ gdb ./segv core.24246
(gdb) bt
#0 0x000000000040055b in main () at segv.c:7
(gdb) frame 0
#0 0x000000000040055b in main () at segv.c:7
7 *p = 0;
(gdb) p p
$1 = (int *) 0x0
-> メモリアドレスが 0x0 となっており、確保されていないメモリ領域にアクセスしたため Segmentation Fault が発生したことが確認できる。