※ 左右のカーソルキーでもページ繰りができます(但しブラウザ依存)
藤原 克則 ( FUJIWARA Katsunori )
無職。自称「フリーダムプログラマー」
受託開発主体の独立系ソフトウェアハウス数社を経てフリーダム化。
前職で、 HPC ( High Performance Computing ) 系システムのために Solaris 向けファイルシステムを実装したのを機に、 OpenSolaris 勉強会に参加。
前職では、 本業の合間を縫って、 「入門Mercurial Linux/Windows対応」とか 「俺のコードのどこが悪い?―コードレビューを攻略する40のルール」 といった書籍の執筆や、 技術系ウェブ媒体への記事の寄稿といったことも。
ホームページ以外にも、 はてなダイアリー (id:flying-foozy) 等で情報発信中。 Twitter アカウント (@flyingfoozy) は細々と運用中。
本資料は、 "Solaris Internals" の第8章を、 各段落毎に「ワンフレーズ」化すること基本としています。
「ワンフレーズ」化対象の段落を識別するために、 以下のような識別情報表記を使用します。
[{章/節番号}/{通し段落番号}]@p.{ページ番号}
例えば、"[8.7/2]@p.451" は、 以下で始まる 「451 ページに配置されている、8.7 節における第2段落」 を指します。
One of the major advantages of using the VM system to ....
列挙の各項目も1段落として扱います。 単一の列挙項目が複数の段落から構成されている場合は、 構成要素の段落を、それぞれ個別の段落として扱います。
識別情報の構成要素は、 紙媒体の "Solaris Internals Second Edition" を元にしています。
なお、個人的に興味深いと思った点に関しては、 勝手に掘り下げた話を展開します。
[8/1]@p.447
仮想メモリ機能は、 他の構成要素も影響する物凄く重要な機能なので、 以降で詳細を説明します
[8.1/1]@p.447 〜 [8.1/4]@p.447
仮想記憶(virutual memory: VM) の利点は:
個人的には、 ユーザプロセス間、ユーザプロセス〜カーネル間で、 アドレス空間が隔離されることによる安全性の向上も、 重要な利点だと思う > 仮想記憶
[8.2/1]@p.448
メモリの利用状況に応じて、 VM 機構がディスク〜RAMの間で頑張るので、 ユーザ空間からはメモリ割り当てについて気にする必要はありません
メモリの「2つのレベル」というのは、 「ディスク/RAM」という記憶クラスのこと? 「VM管理下にあるメモリ/ユーザ空間から見えるメモリ」 というビューのこと?
[8.3/1]@p.448
SunOS で導入された共有ライブラリの概念を更に進めて、 同一プログラムによる複数プロセス間で、 実行バイナリのメモリイメージも共有出来るようになっています
共有ライブラリ(shared library)での共有の場合、 同じ物理メモリに対して、 各プロセス毎に異なる仮想アドレスが割り当てられるので、 位置非依存コード (position independent code: PIC) 化が必要。
実行可能プログラムのテキストセグメントの共有は、 プロセス起動時におけるローディングアドレスが固定されているので、 共有ライブラリと比較すれば簡単な筈。
本文では、「データセグメントも共有している」と言わんばかりの勢いだが、 read-only で共有マッピングしておいて、 改変が発生した時点で writable なプライベートマッピングにする、 copy-on-write の事を指しているのかな?
この辺の話は、 メモリ管理がファイルのキャッシュと統合されている事とも関連が深い筈。
[8.3/2]@p.448
ハードウェアによるメモリ管理の支援によって、 アドレス空間外の領域にはアクセスできないようになっています。
個別に複製を保持していた時分は、 単にアドレス空間が分離されているだけで安全だったのだろうけど、 共有ライブラリや、 ファイルキャッシュとの統合、 copy-on-write を利用したプロセス間でのメモリ共有など、 メモリ効率向上のための共有が進むと、 保護機能もある程度充実させないと駄目なのだなぁ。
[8.4/1]@p.448
物理メモリ(RAM)は、 バックストア(HDD)中の「ファイル+オフセット」に対して、 ページ単位で関連付けが行われる: ユーザプロセスのヒープやスタックは swap ファイルと、 通常ファイルは対応するファイルと関連付け
[8.4/2]@p.449
ダーティーページの再利用は、 内容をバックストアに書き出してから、 クリーンなページの再利用は、 何もせずに再利用すれば良い。
SPARC 系 CPU の最小ページ単位は 8K、 x86 系の最小ページ単位は 4K。
MMU が格納可能な変換エントリ数と、 イマドキのメモリ搭載/消費事情を考えると、 最小ページサイズはちょっと有り得ない気もするが、 ひょっとして、 ダーティー化した際の書き出しコストを気にしている?
[8.5/1]@p.449
MMU ハードウェアによる 「仮想〜物理アドレス変換」は、 ページ単位で実施されます。
[8.5/2]@p.450
MMU におけるページ単位管理により、 様々な用途(プログラムのテキストセグメントや、ヒープ、スタック等)のメモリ領域を、 1つのアドレス空間に貼り付け(mapping)ています。
[8.6/1]@p.450
物理メモリとスワップ領域との間におけるメモリ管理の方針には、 swapping と demand paging という2種類の基本的なものがあります。
[8.6/2]@p.450
対象プロセス群の全メモリを退避する swapping は、 実装が単純な分、復旧時のパフォーマンスが問題です (= 全メモリが復旧してからしか、プロセスを再開出来ない為)。 demand paging はページ単位で退避/復旧するので、 メモリの一部が退避されたプロセスも実行継続可能です。
[8.6/3]@p.450
Solaris は基本 demand paging ですが、 メモリが枯渇してきた場合は swapping も使用します。
[8.7/1]@p.450
Solaris VM は、 ファイル I/O とメモリ管理を統合している点で、 別途ディスクキャッシュを用意していた初期の SystemV 系 UNIX とは違います。
初期の SystemV 系 Unix は兎も角、 現状の Linux や BSD 系 OS と比較した場合はどうなの? by 業務上のライセンス縛りで Linux ソースを読めないエンジニア
[8.7/2]@p.451
ファイルシステムキャッシュの管理が VM 機構に統合されることで、 全ての空きメモリをファイルシステムキャッシュとして使用出来る上に、 「キャッシュ使用量上限のチューニング」 といった不毛な作業から解放されます。
[8.7/3]@p.451 〜 [8.7/7]@p.451
Solaris VMの主な機能は:
っていうか、ここで書くことか? > まとめ
[8.8/1]@p.451
BSD Unix 由来の Solaris VM 機構ですが、 ファイルと仮想メモリの統合という大きな変更によって、 キャッシュ統合や、 VM 階層構造のモジュール化、 複数プラットフォーム/デバイスにおけるコード共有、 といったものがもたらされました。
[8.8/2]@p.451
仮想記憶機構には、多くの特徴的な機能が付加されてきました。
[8.8/3]@p.451
旧来の実装では、 ファイルキャッシュによるメモリ消費によって、 通常プロセスの利用可能メモリ量が影響を受けていましたが、 Solaris 8 からは空きメモリのみがキャッシュ用途に使用されるようになりました。
[8.8/4]@p.451
64bit アーキテクチャの場合、 物理メモリに恒久的な仮想アドレスを割り当てたままにできるようになりました。
仮想アドレスの割り当て/解放は、 MMU エントリの改変も含む(= オーバヘッドが大きい)ことから、 仮想アドレス割り当ての恒久化は効果がありそう。
複数ページに渡る I/O において、 「仮想アドレス的に連続」が前提だったりすると、 結局独自の仮想アドレスマッピングが必要だったりすることも。
ラージページ化することで、 処理対象エントリ数 (= ページ数) を減少させることで、 性能が向上するケースも有り。
[8.8/5]@p.452
メモリサイズがメガバイト級からテラバイト級に移行しており、 より大きなページサイズが求められています。
[8.8/6]@p.452
共有メモリでのラージページ使用や、 複数ページサイズの混在利用等が進められています。
HPC 界隈では、メインメモリ 512G 〜 なマシンも珍しくなくなってきているので、 OS 側もそれに見合った設計/実装が求められる
いっそ、libc や socket 等の標準的な共有ライブラリは、 全てラージページ化すれば良いのに、 という気がしないでもないが、 読み込み単位の巨大化 ⇒ 性能の劣化を気にしているのかな? (read-only 共有だからダーティページの書き出しコストは気にしなくて良い筈)
ひょっとしたら、 メモリ共有とラージページ化のバランスを取る上で、 メモリへの共有ライブラリの読み込みの際に、 Hot Spot が複数ページに散在しない (= Hot Spot が集約されていない状況でラージページ化した場合、 実行頻度が低いコードがメモリを不要に占有してしまうため) ように、 プロファイル情報ベースで稼動環境毎の再配置、 といった話が今後は出てくるかも?
ちなみに、 関数単位で似たようなこと (= バイナリ改変による Hot Spot 集約) をやる機能は、 20 世紀時分は HP だったか DEC が出していた筈。
[8.8/7]@p.452
CPU/メモリの搭載量が増えると、 各ハードウェア要素間の相互連携コストに不均衡が生じる場合があります。
[8.8/8]@p.452
Solaris 9 以降では、 locality group (lgroup) という概念に基づく MPO (Memory Placement Optimization) 機能が導入されました。
SUN の SPARC サーバは、 比較的エントリークラス (2 CPU SunFire 等) でも、 複数 CPU パッケージ = NUMA 構成な場合が多いけど、 Fujitsu のサーバは、 相当高額なモデルにならないと、 複数 CPU パッケージでも NUMA 構成にならないのは、 会社の個性という噂が。
64bit x86 系は、 複数 CPU パッケージなら NUMA になるということなので、 当該構成のホスト上で lgrpinfo コマンドで確認を。
最近は、 スケールアウト方向に振るのがトレンドだから、 NUMA 構成で複数 lgroup な構成に触れる機会はあまり無いのかな?
メモリ割り当て処理等で、 lgroup 横断を意識した処理 (ポリシーの動的切り替え等) とかが散見されて、 なかなか面白い > カーネルソース
例えば、 カーネルページ割り当てを受けるための page_create_va() @usr/src/uts/common/vm/vm_page.c を契機とした page_get_freelist() @usr/src/uts/common/vm/vm_pagelist.c にかけての処理では、 以下のような lgroup 配慮が含まれている。
page_create_va() @usr/src/uts/common/vm/vm_page.c
page_get_freelist() @usr/src/uts/common/vm/vm_pagelist.c
lgrp_mem_choose() @usr/src/uts/common/os/lgrp.c
lgrp_memnode_choose() @usr/src/uts/common/os/lgrp.c
[8.8/9]@p.452
稼働中に物理メモリの抜き差しが最大限実施出来るように、 メモリ管理機構が拡張されました。
[8.8/10]@p.452
ユーザ空間向けのメモリ領域は解放/再配置が可能ですが、 カーネル向けのメモリ領域は、 「カーネルケージ」 (kernel cage) に囚われています。
[8.8/11]@p.453
物理メモリの容量増減は、 RCM(Resource Configuration Manager) によって提供され、 ユーザ空間でもイベント通知を受理可能です (Oracle 9 はメモリ容量の増減に対応)
そもそも、 その辺で手に入るハードは稼働中のメモリモジュール挿抜に対応してないのでは? ラックマウントレベルでもエントリクラスは駄目じゃね? DTrace とかで実際にイベント伝播のフローとかを追いかけてみたいので、 是非対応ハードを勉強会等の機会に提供して頂きたく(笑) > Oracle 様
[8.8/10]@p.452 は、 個人的にちょっと理解不十分。 「カーネルページがカーネルケージに囚われているのは、 再配置不可だから」と「どれでも任意のメモリボードが抜けるけど、 抜けるのは1つだけ」の掛かり受け関係がイマイチわからん .... orz
[8.8/12]@p.452
新しいメモリ割り当て実装として、 SMP 構成における各 CPU のキャッシュ等にも配慮した 「Slab Allocator」が採用されました。
複数コア/複数ソケット環境が、 比較的安価になってきているので、 性能的な観点からも、 CPU キャッシュに配慮してくれるのは嬉しい話。
最近は、 SLAB 代替として SLUB なるアロケータもあるらしい (組み込み向けの簡易版に SLOB アロケータもある模様)。
なお、ここで言う「SLAB 代替」の SLAB が、 Solaris で言う Slab Allocator を指すのか、 「メモリ切り出し処理」に対する一般名詞としての SLAB を指すのかは不明。 情報求む > 詳しい人