24.4. FreeBSD VM システム

原作: Matthew Dillon . 6 Feb 1999

24.4.1. 物理メモリ管理 --- vm_page_t

物理メモリはページ単位に, vm_page_t構造体を用いて管理されます. 物理メモリのページは, ページキューの一つに存在する, それぞれの vm_page_t 構造体の配置によって分類されます.

ページは, wired(ワイヤード), active(活性状態), inactive(非活性状態), cache(キャッシュ状態), free(使われていない状態)の 各状態をとります. wired 状態を除いて, ページは通常 その状態を示す二重連結リストのキューに置かれます. wired 状態のページがキューに置かれることはありません.

FreeBSD は, ページカラーリング(page coloring)を実装するため, cache 状態, free 状態にあるページ用に, さらに複雑なページキューを実装しています. その各々の状態は, プロセッサの L1, L2 キャッシュサイズに応じて最適化された 多重キューを利用します. FreeBSD は, 新たなページを確保(allocate)することが 必要になった場合に確保される VM オブジェクトのために, L1, L2 キャッシュに対して合理的にアライン(align)されたページを 得ようと試みます.

加えて, ページは参照カウントとともに保持され, ビジーカウントとともにロックされます. VM システムは, ページフラグとして PG_BUSY を使う ``完全ロック状態'' も実装しています.

一般的には, 各々のページキューは最長不使用 (LRU) 方式で動作します. ページは普通, 最初に wired, もしくは active 状態に置かれます. wired 状態の場合, そのページはどこかにあるページテーブルに 関連づけられています. VM システムはアクティブなキュー内のページをスキャンし, wired 状態のページにエイジング (訳注: ページ参照頻度を量る手法の一つ; aging) を施します. そして, そのページはあまりアクティブでないキューへ 移動することになります. cache キューに移動させられたページは, 再利用の候補になっている VM オブジェクトに割り付けられています. free キューにあるページは, 完全に自由の状態にあります. FreeBSD は, free キューにあるページ数を最小限にとどめようと 試みますが, 割り込み発生時のページ確保を融通するため, 完全に自由なページをいくつか持っていなければなりません.

プロセスがページテーブルに存在しない, ページキューの一つ(例えば, inactive, cache キュー等)に 存在するページをアクセスしようとしたとき, 比較的負荷の小さなページ再活性化フォールトが起こります. システムメモリに全く存在していないページの場合は, ディスクからページを読み出す間, そのプロセスはブロック(block)されます.

FreeBSD は, ページキューを動的に調節し, 同期済(clean)のページ, 同期していない(dirty)ページの分類を 合理的に保つのと同様に, それぞれのキューにあるページが合理的な 比率に保つように試みます. 再バランス化処理が起こる量は, システムのメモリ負荷に依存します. この再バランス化処理は ページアウトデーモンによって実装されていて, (補助記憶とページを同期して)同期していないページの クリーニングすることや, (LRU キュー内でのページ位置を再配置したり, ページをキューの間を移動することで)ページが頻繁に 参照状態にあることに注目すること, キューを均等にするための キュー間ページ移動等を伴います. ページが実際にどれだけ使われているかを決定するために, FreeBSD の VM システムは, ページの再活性化フォールトを 自発的に, 合理的な数だけ発生します. これは, ページをスワップアウトしたり, クリーニングする時期を より良く決めることに繋がります.

24.4.2. 統合バッファキャッシュ --- vm_object_t

FreeBSD は, 一般化した ``VM オブジェクト'' という考え方を実装しています. VM オブジェクトは, 様々な種類の補助記憶(backing store) --- 補助記憶なし, スワップ, 物理デバイス, ファイル, に割り付けられます. ファイルシステムは ファイルと関連するインコアデータを管理するのに, 同じ VM オブジェクトを利用するため, 統合バッファキャッシュと呼ばれます.

VM オブジェクトは, シャドウ化 することができます. シャドウ化とは, オブジェクトがそれぞれ互いの上に スタック(stack)されるということです. 例えば, MAP_PRIVATE mmap() の 動作を実装するために, ファイルに割り付けられた VM オブジェクトの上にスタックされた, スワップに割り付けられた VM オブジェクトが存在しているでしょう. このスタッキングは, fork されたアドレス空間のための 様々な共有属性, コピーオンライト(訳注: ページ共有のための 手法の一つ; cow,copy-on-write) を実装するのにも利用されています.

vm_page_t は, 同時に一つの VM オブジェクトしか割り付けられることが できないことに注意しなければなりません. VM オブジェクトのシャドウ化は, 複数のインスタンスが同じページに 共有できるように実装されています.

24.4.3. ファイルシステム I/O --- struct buf

補助記憶にファイルを使う VM オブジェクトのように, v ノードを使う VM オブジェクトは通常, 処理されているかどうかという情報を, VMシステムが管理する処理情報から独立して 管理される必要があります. 例えば, VM システムが物理ページと補助記憶を同期させようとしたとき, VM システムは, 実際に書き戻す前に, ページがクリーニング済であるという マークを付ける必要があるわけです. さらに, ファイルシステムは, KVM 内で操作できるように, ファイルや, ファイルメタデータの一部分を KVM にマッピングすることが できなくてはなりません.

これを管理するために使われる実体は, ファイルシステムバッファ, struct buf, bp として知られています. ファイルシステムに VM オブジェクトの一部を操作することが 必要となるときは通常, オブジェクトの部分が struct buf に マッピングされ, KVM に struct buf 内のページがマッピングされます. 同じ方法で, ディスク I/O はオブジェクトの部分を バッファ構造体内にマッピングし, その時バッファ構造体上の I/O を 発行することで発行されます. 基礎となっている vm_page_t は, I/O 処理の間 ビジー(busy)状態になります. ファイルシステムにも 独立したビジー状態があり, それはハードウェア上の VM ページの代わりに ファイルシステムバッファで動作する ファイルシステムドライバのコードに とって有用です.

FreeBSD は, マッピングを保持するためにある量に制限された KVM を 予約していますが, KVM がマッピングを保持するためだけに使われ, キャッシュデータの能力を制限しないということは 明確にされるべきでしょう. 物理データキャッシュを行うことは厳密に vm_page_t の機能になっており, ファイルシステムバッファの機能ではありません. しかし, ファイルシステムバッファは placehold I/O に使われるため, それは実質的に同時処理可能な I/O 処理量を制限します. 通常は二, 三千のファイルバッファが利用可能ですから, このことは問題にならないでしょう.

24.4.4. マッピングページテーブル --- vm_map_t, vm_entry_t

FreeBSD は, 物理ページテーブルの形態を VM システムと分離しています. ハードウェア上にある全てのプロセス毎のページテーブルは, その場その場で再構成され, 通常, 使い捨てだとみなされています. KVM を管理するような特殊なページテーブルは, 最初に永続的な確保が 行われ, これらのページテーブルが破棄されることはありません.

FreeBSD は, vm_objects の部分を, 仮想メモリのアドレス範囲に vm_map_tvm_entry_t 構造体を通して割り付けます. ページテーブルは, vm_map_t /vm_entry_t/vm_object_t という階層から 直接つくられます. ``物理ページは, 直接一つの vm_object に 割り付けられる'' と私が述べたことを思い出して下さい. ええと, そうですね, しかしそれはいつでも完全に当てはまる, というわけでもないのです. vm_page_t のは, 実際に割り付けられた ページテーブルにもリンクされています. 一つの vm_page_t は ページテーブルが呼ばれた時, いくつかの pmaps と リンクされることがあります. しかし, そのような階層的な割り付けは, 同じ vm_page_t を参照するオブジェクト内の, 同じページへの参照全てを保持しているため, その結果, 常にバッファキャッシュの統合を得ることができるわけです.

24.4.5. KVM メモリマッピング

FreeBSD は, 様々なカーネル構造体を保持するため, KVM を利用します. ファイルシステムバッファキャッシュは, KVM 内で最も大きなものです. それはつまり, struct buf の実体に対するマッピングに他なりません.

Linux と異なり, FreeBSD は全ての物理メモリを KVM にマッピングしません. これは, FreeBSD が 32 ビットプラットフォームで 4G バイトまでの メモリを扱える, ということを意味します. 実際, MMU がそれを可能にしているならば, 理論上, FreeBSD は 32 ビットプラットフォームで 8TB までのメモリを扱うことができることになります. しかし, 大部分の 32 ビットプラットフォームは 4G バイトの RAM しか マッピングできないようになっている, ということには議論の余地があるでしょう.

KVM は, いくつかのメカニズムによって管理されています. 中心となっているのは, ゾーンアロケータ(zone allocator)です. ゾーンアロケータは, 特定の構造体型を確保するために KVM の部分(chunk)を得て, 一定の大きさのメモリブロックに分割します. vmstat -m コマンドで, ゾーンによって 分割された, 現在の KVM 利用状況一覧を得ることができます.

24.4.6. FreeBSD VM システムのチューニング

FreeBSD カーネルでは, 動的に自分自身をチューニングするために, 協調的な努力が行なわれています. 普通は, maxusers NMBCLUSTERS という カーネルオプション, つまり, /usr/src/sys/i386/conf/CONFIG_FILE で 指定されるもの以外, 変更する必要はありません. 可能なカーネルオプションの一覧は, /usr/src/sys/i386/conf/LINT に 記載されています.

大きなシステムに対しては, maxusers を増やしたいと思うかも知れませんね. この値は普通, 10 から 128 の間の値にします. maxusers を増やしすぎるとシステムの利用可能な KVM がオーバフローしてしまい, 予測できない動作に陥ってしまうことに注意して下さい. maxusers はある適度な値にとどめておいて, 特定のリソースを制御する NMBCLUSTERS のような, 他のオプションを増加させる方が良いでしょう.

もし, システムが負荷の高いネットワーク用途に使われるなら, NMBCLUSTERS を増やしたいと望むことでしょう. この値は普通, 1024 から 4096 の間です.

NBUF パラメータも, 伝統的にシステムの規模を決めるのに使われます. これは, システムがファイルシステムバッファを I/O のために マッピングするのに使われる, KVA の大きさを決めるのに使われます. このパラメータは, 統合バッファキャッシュには何の影響も与えません. これは 3.0-RELEASE 以降のカーネルでは動的にチューニングされるため, 普通は手作業で調整されるべきものではありません. NBUF パラメータは, 指定しようとしないことを推奨します. システムに選択させれば良いのです. 小さすぎる値は極端に非効率的なファイルシステム動作を招き, 一方で, 大きすぎる値は wired 状態のページを数多くつくりだし, ページキューを枯渇させてしまうでしょう.

デフォルトでは, FreeBSD カーネルは最適化されていません. カーネルコンフィグにある makeoption ディレクティブを使って 最適化とデバッグフラグをセットすることができます. ただし, それによって得られる大きな (7MB 超の)カーネルを相手にするのが嫌なら, -g オプションは使ってはいけません.

    makeoptions    DEBUG="-g"
    makeoptions     COPTFLAGS="-O2 -pipe"

sysctl は, 実行時にカーネルパラメータをチューニングする 手段を提供しています. しかし, 普通は sysctl 変数, 特に VM に関連したものを変更する必要が 生じるようなことはありません.

実行時の VM とシステムのチューニングは, 比較的単純です. まず, 可能ならば UFS/FFS ファイルシステムで softupdates を使いましょう. /usr/src/contrib/sys/softupdates/README のファイルに, 設定方法に関する手順(と制限)について書かれています.

次に, 十分なスワップを設定します. ``作業'' ディスクを含む 各物理ディスク装置毎に一つずつ (最大四つまで)のスワップパーティションを 設定すべきです. 少なくとも, メインメモリの 2 倍の スワップ空間が望ましく, メモリがあまりない場合には, おそらくそれより多く必要になります. また, スワップパーティションのサイズは, 後でパーティションをつくり直しする必要がないように マシンに設定したいメモリ設定の最大値を基準に 決めるべきでしょう. もし, クラッシュダンプをとりたい場合, スワップパーティションは最低限メインメモリと同じの大きさで, /var/crash にはダンプを保持するのに十分な 空きがなければなければなりません.

NFS 経由のスワップは, -4.x 以降のシステムで完全に動作しますが, NFS サーバ側では, ページングがその負荷の主な原因になることに 注意しなければなりません.