このページの本文へ

基礎から覚える 最新OSのアーキテクチャー第7回

Windowsのメモリー管理をx86の仕組みから読み解く

2011年11月17日 17時24分更新

文● 塩田紳二

  • この記事をはてなブックマークに追加
  • 本文印刷

64bitモードでのメモリーアクセスの仕組み

 Windows上でプロセスが生成される際には、作業領域が作られてページディレクトリテーブル、ページテーブルがそれぞれ作られる。CR3レジスターには、ページディレクトリテーブルを示す値が設定される。OSはアプリケーションをメモリーに入れる前に必要な物理メモリーを割り当てるが、これは「ページディレクトリテーブルとページテーブルの作成」という形で行なわれる。

 なお、前回解説した「PAE」で物理メモリー空間が36bitに増えている場合には、テーブルが1段階増える。ページディレクトリテーブルの前に「ページディレクトリポインタテーブル」があり、CR3はこのテーブルの開始位置示す。

 このとき32bitのリニアアドレスは、2bit、9bit、9bit、12bitに分割される。ページテーブルの各エントリは24bitのアドレスを出力するために、リニアアドレスの下位12bitと合わせて36bitアドレスになる。しかしリニアアドレスは32bitのままなので、プログラムが扱えるメモリー空間は4GBのままだ。

 64bitモードでもテーブル段数は増えており、4段階のテーブルで64bitリニアアドレスを変換して36bitのページアドレスを出力し、リニアアドレス下位12bitと合わせて48bitの物理メモリーアドレスを得ている。

 x86はなぜ、多段階のテーブルを使う構造を採用したのか? それは、ページによっては複数のプロセスが共有する場合があるためだ。OSによって違いはあるが、例えば、同一のプログラムが複数同時に実行された場合、そのプログラムが同一であるならば、ページを共有してメモリーを節約できる。

 そして共有するページの数がある程度の数になるならば、ページテーブル自体も共有できる。そしてページディレクトリテーブルを使うことで、複数のページテーブルの位置を自由に指定できることになる。Windowsでは、DLLなどの共有コードはメモリー内の上位アドレスに置き、プログラムコードは下位アドレスに置く。こうした配置はページディレクトリテーブルで指定でき、ページテーブル自体はプログラムの開始位置とは無関係に指定できる。

 なお、こうしたアドレス変換は、テーブルをアクセスするたびにメモリーを読むことになるため、たとえテーブルがCPUのキャッシュに保存されていても、いくつかのクロックサイクルを必要とする。この作業を高速化するために、CPU内部には「TLB」(Translation Lookaside Buffer)と呼ばれる特別なキャッシュがある(関連記事)。

 TLBには直前にアクセスされたページディレクトリテーブルや、ページテーブルなどのエントリが記録されている。同じエントリが参照されると(リニアアドレスの上位20bitで判定できる)、TLB内の情報を使ってアドレスを変換する。ただしこのTLBは、CR3レジスターへの書き込みで書き換えられるほか、カーネルモード(リング0)のプログラムから無効にできる。

スワップイン・スワップアウトを避けるには

 仮想記憶機構は、現在実行中のプログラムに必要なメモリーが足りなくなると、ほかのプログラムに割り当てたページの中身をストレージ(ここではHDDを例にとる)に書き出して、空いたページをプログラムに割り当てる(スワップアウト)。そのときページテーブルエントリには、ページが物理メモリーに存在することを示すビット(存在ビット)があるので、これをオフにしてページがスワップされたことを示す。この操作はOSが行なう。

 別のプログラムに制御が移ったとき、メモリーにアクセスしようとアドレス変換をする際にページテーブルエントリを見ると、存在ビットの状態を見るだけでページがスワップアウトされていることがわかる。このとき、CPUには割り込みがかかり、OSのメモリー管理機構に制御が渡って、HDDに保存していたページデータを空いているページに書き戻す(スワップイン)。もしここで空きページがないと、同じように止まっているほかのプロセスのページを書き出して、無理にでも空きをつくる。

 このようなメカニズムなので、物理メモリーが足りない状態でも、アプリケーションに大きなメモリーを割り当てることができる。だが、ある程度メモリーの空きがないと、HDDへの読み書きが連続して起こり、パソコンの処理速度が下がってしまう。これを「スラッシング」という。

 通常のパソコン使用時は、短時間で起動して終了するプロセスなどがあるため、平均するとメモリーには空きがあり、スラッシングが続くことは少ない。しかし物理メモリー量を超えるメモリーを使おうとすると、途端に処理速度が下がるのはどうしようもない。

 これを回避するために、ページをスワップアウトする際にはなるべく、次に動くまでの時間の長いプロセスや、長い間止まったままのプロセスなどを選び、影響が少なくなるようにする。長い間止まっている(たまにしか動かない)プロセスならば、動き出すまでの時間が多少長くてもかまわないだろう、というわけだ。

 なお同様の仕組みは、プログラムが初めてメモリーをアクセスする場合にも起きている。最初からすべてのメモリーにページを割り当てず、実際にアクセスが起こった場合にのみ割り当てることで、実際には使われない領域にまでページを割り当ててしまわないようにするためだ。ページテーブルの各エントリの存在ビットは最初オフになっており、最初のアクセスが起こったときに例外が発生して、カーネル側で必要なページを割り当てるわけだ。

カテゴリートップへ

この連載の記事

注目ニュース

ASCII倶楽部

最新記事

プレミアムPC試用レポート

ピックアップ

ASCII.jp RSS2.0 配信中

ASCII.jpメール デジタルMac/iPodマガジン