Linuxカーネルが成長し、成熟するにつれ、技術解析アプリケーションや、さらには巨大なデータベースを扱うような、非常に大きなシステムでLinuxを使用したいと考えるユーザーが増えてきました。こうした企業レベルのアプリケーションでは、高パフォーマンスのために大容量のメモリが要求されます。2.4 Linuxカーネルにもかなり大容量のメモリを取り扱えるような機構があったのですが、2.5カーネルにはさらに多数の変更が加えられ、より効率的に、より大容量のメモリが扱えるようになりました。
Linuxのメモリ・マネージャでは、プロセスが使用するメモリの物理ページをページテーブルが追跡し、このテーブルが仮想ページを物理ページにマッピングします。こうしたページの一部は長期間に渡って全く使われず、スワップアウトの対象候補となるのですが、スワップアウトする前にそのページをマップしているプロセスを全て見つける必要があります。そうしないと、そのプロセス中でそのページに対するページテーブルのエントリーを更新することができません。Linux 2.4カーネルでは、そのページがそのプロセスによってマップされているかどうかを判断するために、どのプロセスのページテーブルもトラバースする必要があるので、大変な仕事でした。システム上で実行されているプロセスの数が増えるにつれ、こうしたページをスワップアウトするための作業もどんどん大きくなったのです。
この問題を解決するために、2.5カーネルにはリバース・マッピング、つまりRMAPが実装されました。リバース・マッピングは、ある(メモリの)物理ページをどのプロセスが使っているかを見つけるための機構です。全てのプロセスに対するページテーブルをトラバース代わりに、メモリ・マネージャには、各物理ページに対して、(現在そのページをマップする全プロセスの)ページテーブル・エントリー(PTE)へのポインターを持つリンク付のリストがあるのです。リンクされたこのリストはPTEチェーンと呼ばれます。PTEチェーンを使うと、ページをマッピングしているプロセスを見つけるスピードが非常に速くなります。これを図1に示します。
図1. 2.6でのリバースマッピング
もちろん何事もタダではありません。リバース・マッピングを使ってパフォーマンスを向上するには代償も必要なのです。最も明らかな代償はメモリのオーバーヘッドが増すということです。リバース・マッピングを追跡するためには一部のメモリを使用する必要があります。PTEチェーンの各エントリーはページテーブルへのポインターを保存するために4バイトを使用し、チェーンの次のエントリーへのポインターを保存するためにさらに4バイトを使用します。このメモリは低位メモリ領域から使う必要があるのですが、32ビットのハードウェアでは低位メモリはやや限られています。場合によってはリンク付リストを使う代わりに単一のエントリーを使うようにまで最適化することができるのですが、この方法はページダイレクトアプローチと呼ばれています。もしそのページへのマッピングが一つしかない場合には、リンク付リストの代わりに「ダイレクト」と呼ばれる単一のポインターを使います。この最適化が使えるのは、1つのプロセスのみがそのページをマップしている場合だけです。もし他のプロセスが後でそのページをマップしている場合には、そのページをPTEチェーンに変換する必要があります。与えられたページに対してこの最適化が実行中の場合にはメモリ・マネージャに知らせるためのフラグが立ちます。
リバース・マッピングによって複雑になる点もいくつかあります。プロセスがページをマップする時には必ず、こうしたページ全てに対してリバース・マッピングを確立する必要があるのです。同様に、プロセスがページのマップを解除すると、対応するリバース・マッピングも削除する必要があるのです。これは特にexit時には、共通に行われます。こうした操作全てはロックされた状態で行う必要があります。forkやexitを頻繁に行うアプリケーションでは、これは非常に高くつき、大きなオーバーヘッドとなります。
多少の代償が必要とはいえ、リバース・マッピングはLinuxのメモリ・マネージャには貴重な変更となりました。この手法を使う事で、ページをマップしているプロセスを見つける深刻なボトルネックが最小ですむようになったのです。リバース・マッピングのおかげで、大きなアプリケーションがカーネルに対して巨大なメモリを要求する場合や、複数のプロセスがメモリを共有している場合でも、システムのパフォーマンス低下が無くなり、拡張性も確保できるのです。リバース・マッピングはLinuxカーネルの将来バージョンに含められるように、現在もさらに改善が検討されています。
典型的な使い方では、x86システムではメモリ・マネージャはメモリを4 KBのページとして扱いますが、実際のページサイズはアーキテクチャに依存します。大部分の使い方では、メモリ・マネージャが一番効率良くメモリを扱えるのは、このサイズのページだと言えます。ところが一部のアプリケーションでは非常に大量のメモリを使います。巨大なデータベースはその好例と言えます。各プロセスがマップする各ページに対して仮想アドレスを物理アドレスにマップするためには、ページテーブル・エントリーも作る必要があります。もし1 GBのメモリを4 KBのページでマップするアプリケーションだとすると、ページを追跡するには262,144のページテーブル・エントリーが必要になります。もしページテーブル・エントリーがそれぞれ8バイトを消費すると、マップするメモリ1 GB毎に2 MBのオーバーヘッドになります。これだけでもかなりのオーバーヘッドですが、そのメモリを複数のプロセスが共有している場合にはもっとひどくなります。この場合には、その同じ1 GBのメモリをマップしているプロセスそれぞれが自分のページテーブル・エントリーに2 MB分のメモリを消費してしまうのです。プロセスが多数ある場合にはオーバーヘッドで浪費されるメモリがアプリケーションが使用するメモリを上回ってしまうかも知れません。
この問題を軽減するには、使用するページのサイズを大きくする事です。最近のプロセッサーの大部分は少なくともラージページとスモールページをサポートしていますが、それ以外のサイズもサポートしているものもあります。x86では、物理アドレス拡張(PAE)がオンになっているシステムであれば、ラージページのサイズは4 MBまたは2 MBです。上の例でラージページのサイズとして4 MBを使うとすると、先の1 GBのメモリをマップするのに262,144も必要なく、256のページテーブル・エントリーだけですむのです。これはつまりオーバーヘッドとしては2 MBも要らず、2,048バイトだけですむという事になります。
大きなサイズのページを使うとtranslation lookaside buffer (TLB)の使い残しも減るので、パフォーマンスも向上します。TLBはページテーブルに対する一種のキャッシュですが、これを使うとテーブルにリストされているページに対する仮想メモリから物理メモリへの変換が、より高速に行えるようになるのです。当然ながらTLBが保持できる変換の数は限られます。ページが大きくなれば実際のページ数は少なくなり、より大量のメモリを収容できるようになります。ですから使うページが大きければ大きいほど、(ページサイズが小さい時に比べて)TLBで参照されるメモリの量も多くなるのです。
32ビットのマシンでは通常、ページテーブルは低位メモリにしか保存できません。この低位メモリは物理メモリの最初の896 MBに限られており、カーネルの残りの大部分もこの領域を要求します。アプリケーションが大量のプロセスを使用し、大量のメモリをマップするような状況では、低位メモリはすぐに残りが僅かになってしまいます。
ところが今度2.6カーネルに入ったHighmem PTEと呼ばれる設定オプションを使うと、ページテーブル・エントリーを高位メモリに置けるようになります。これによって、どうしても低位メモリに置く必要のある他のカーネル・データ構造のために低位メモリを解放する事ができるのです。その代わり高位メモリに置かれたページテーブルのエントリーを使うプロセスは少し遅くなりますが、非常に多くのプロセスが実行しているようなシステムでは、ページテーブルを高位メモリに保存することで、低位メモリをより有効に使うことができるのです。
図2. メモリ領域
安定性が向上したことも2.6のメモリ・マネージャで改善された重要な点です。2.4カーネルがリリースされた直後から、ユーザーはメモリ管理に関連した安定性の問題に悩む事になりました。メモリ管理がシステム全体に及ぼす影響を考えると、安定性は最も重要性が高い問題と言えます。問題の大部分は解決されたのですが、その解決方法は本質的にはメモリ・マネージャの中身を取り除き、その代わりに簡略化したものと置き換えただけでした。このためLinuxの販売業者には自分たちのディストリビューション用にメモリ・マネージャを改善する余地ができたわけです。これは言い換えると、2.4のメモリ管理機能はどのディストリビューションを使っているかによって大きく異なると言う事でもありました。そうした事態がまた起きるのを避けるために、2.6のカーネル開発で一番厳しく検証された領域はメモリ管理なのです。新しいメモリ管理用のコードはローエンドのデスクトップ・システムから、大規模で企業レベル、複数プロセッサーのシステムに至るまで、あらゆるシステムでテストされ、最適化されています。
Linux 2.6カーネルで行われたメモリ管理の改善に関連してこの記事で説明したのは、機能のごく一部でしかありません。変更されたものの大部分は小さなものですが、重要性は変わるわけではありません。こうした変更すべてによって2.6カーネルのメモリ・マネージャのパフォーマンスが向上し、また効率や安定性も向上しているのです。Highmem PTEやラージページといった一部の変更はメモリ管理が引き起こしたオーバーヘッドを減少させます。その他、リバース・マッピングなどのような変更は、ある非常に難しい領域でのパフォーマンスを向上させます。こうした具体的な例を選んで説明したのは、Linux 2.6カーネルがより企業向きのハードウェアやアプリケーション用として、いかに調整・機能向上されているかがよく現れていると思ったからです。
- Martin BlighとDavid Hansenによる論文「 Linux Memory Management on Larger Machines」が2003年Linuxシンポジウムで発表されました。
- Red HatのRik van Rielが、何GBものRAMがあるマシンで仮想メモリ・サブシステムがうまく動作しない問題の一端を、2003年オタワLinuxシンポジウムで発表したプレゼンテーションTowards an O(1) VMで説明しています
- Mel GormanがLinux仮想メモリ・マネージャを理解するための
資料 (US)を提供しています。この資料はPrentice Hallから本としても出版されています。
- Kernel/Analysis HOWTOにはLinuxのメモリ管理についてのページ (US)があります。
- LWN.netにはLinuxカーネルのラージページのサポートに関する記事と、2.5カーネルに吸収されたオブジェクト・ベースのリバース・マッピングVMに関する記事があります(共に英語)。
- IBM Systems JournalにはIBMでソフトウェアのテストがどのように行われているかについて多数の記事が掲載されています (US)。
- IBMのLinux Technology CenterはLinux開発コミュニティと直接作業しています。
-
Linux at IBMサイトでは全IBMからのLinuxに関するニュースを提供しています。
- 2.6リリースに先立ち、IBMdeveloperWorksでは「Linux 2.6へ」で新しいスケジューラーやNative Posix Threading Library (NPTL)を含めた、新しいカーネルの重要な機能のいくつかについて検証しています(developerWorks 2003年9月)。
-
LTP testing of the 2.4 Linux kernelを読んでみてください(developerWorks 2003年12月)。
- Paul Larson が2.4から2.6へのカーネル開発における改善も紹介しています(developerWorks 2004年2月)。
- 2.6カーネルでスピードがどの程度改善されているかについて、2.4と2.6でのWeb処理を読んでみてください(developerWorks 2004年2月)。
-
developerWorks LinuxゾーンにはLinux開発者のための資料がさらに豊富に用意されています。
- Developer BookstoreのLinuxセクションには多彩な技術書があります。