セグメント

Linuxのブートプロセスを見る」を見たら、セグメントについてわからなかったことが理解できたので、メモ。

私が混乱していた原因はたった1つ。

  • リアルモードとプロテクトモードではセグメントレジスタの使い方が異なるから。

以下では、その詳細について述べる。

リアルモードでのセグメントレジスタの使い方

セグメントベースにみなしたい番地を、直接セグメントレジスタに放り込む。
つまり、今アクセスしたい番地がDS CS:offsetであれば、実際に実行されるアドレスは CS + offsetで与えられる。

プロテクトモードでのセグメントレジスタの使い方

セグメントレジスタには、セグメントテーブルの何番目のエントリを使用するのかを示すインデックスと、セグメントの設定が入る。このインデックスと設定の組を、セグメントセレクタという。

なお、セグメントセレクタの構造体は、以下のようになっている:

インデックス(15bit目-3bit目) TI(2bit目) メモリ保護機能が参照するデータ(1bit目-0bit目)

せっかくなので、QemuGrubから私のプログラムに処理が移った直後のセグメントレジスタの値を以下に示すので、読解してみよう。

 ES 0x0010
 CS 0x0008
 SS 0x0010
 DS 0x0010
 FS 0x0010
 GS 0x0010

これを見ると、私のプログラムはCSはセグメントテーブルの1番目のエントリを参照しており、他のセグメントレジスタは2番目のエントリを参照していることがわかる。このような作りになっているのは、16ビットから32ビットにCPUが拡張された際、セグメントの扱う領域も拡大してしまったから、なんだとか。

フラットメモリモデル

フラットメモリモデルは、現在主流のOSで採用されているメモリモデル。「フラット」っていうのは、全てのセグメントのセグメントベースを0x00000000番地、セグメントリミットを0xffffffff(4GB)に設定することをいう。このままだとリアルモードみたいに全てのメモリ空間が干渉しあってしまう。そこで、保護機能としてページングを利用する。

ページングディスクリプタ

ページングディスクリプタは、CR3に格納される。ちなみに、GDTをCPUに認識させるときはlgdt命令を使用したが、CR3への代入にはmov命令を使う。現在のOSでは、プロセスのコンテキストスイッチ時に適宜CR3レジスタに値をいれている(はず)。