文書の過去の版を表示しています。
<color red>自分用の適当翻訳ですので細かい所は間違いがあると思います.原文 (Documentation/cgroups/memory.txt) をお読みください.</color>
メモリリソースコントローラ
NOTE: The Memory Resource Controller has generically been referred to as the memory controller in this document. Do not confuse memory controller used here with the memory controller that is used in hardware. (For editors) In this document: When we mention a cgroup (cgroupfs's directory) with memory controller, we call it "memory cgroup". When you see git-log and source code, you'll see patch's title and function names tend to use "memcg". In this document, we avoid using it.
メモリコントローラの利点と目的
メモリコントローラは,タスクのグループのメモリに関する動作をシステムの他の部分から隔離する.LWN記事では,メモリコントローラがどのように使われそうかについての記載がある.メモリコントローラは以下のように使うことができる.
- アプリケーションやアプリケーションのグループを隔離する
メモリを大量に消費するアプリケーションを隔離し,少ないメモリ消費量に制限する - メモリの消費量を制限した cgroup を作成する; これはブート時に mem=XXXX と指定して起動する事に対する良い代替策として使えるだろう
- 仮想化ソリューションで仮想マシンに対して割り当てるメモリ量をコントロールすることができる
- CD/DVD への書き込みソフトが,使用可能なメモリが足りないのが原因で書き込みに失敗しない事を保証するように,システム上の書き込みソフト以外のものが使うメモリ量をコントロールできる
- 他にもいくつかのユースケースが存在する; それを見つけて面白い使い方をしよう (VM サブシステムの学習とハックのために)
Current Status: linux-2.6.34-mmotm(development version of 2010/April)
機能:
- 匿名ページ,ファイルキャッシュ,スワップキャッシュの使用量のカウントと制限
- グローバルな LRU がなく,memcg 毎の専用の LRU にリンクされるページ
- オプションで,メモリ+スワップの使用量のカウントと制限
- 階層構造
- ソフトリミット
- タスクを移動することによりカウントの移動 (再カウント) が選択可能
- 使用量のしきい値の通知
- oom-killer が knob と oom-notifier を無効にすること
- root cgroup には制限のコントロールがない
カーネルメモリのサポートは作業中であり,現在のバージョンでは基本的な機能のみ提供されている.(Section 2.7 参照)
コントロールファイルの簡単なまとめ
tasks # タスク(スレッド)を(訳注:cgroupに)参加させる.そしてスレッドのリストを表示する cgroup.procs # プロセスのリストを表示する cgroup.event_control # event_fd() 用のインターフェース memory.usage_in_bytes # メモリの現在の res_counter の消費量を表示する (See 5.5 for details) memory.memsw.usage_in_bytes # メモリ+スワップの現在の res_counter の消費量を表示する (See 5.5 for details) memory.limit_in_bytes # メモリ消費量の制限値を設定する/表示する memory.memsw.limit_in_bytes # メモリ+スワップ消費量の制限値を設定する/表示する memory.failcnt # メモリ消費量が制限値にヒットした回数を表示する memory.memsw.failcnt # メモリ+スワップ消費量が制限値にヒットした回数を表示する memory.max_usage_in_bytes # 記録されているメモリ消費量の最大値を表示する memory.memsw.max_usage_in_bytes # メモリ+スワップ消費量の最大値の記録を表示する memory.soft_limit_in_bytes # メモリ消費量のソフトリミットを設定する/表示する memory.stat # 様々な統計値を表示する memory.use_hierarchy # カウントの階層構造の有効/無効を設定する/表示する memory.force_empty # 親グループへチャージを強制移動するトリガ memory.swappiness # vmscan の swappiness パラメータを設定する/表示する (See sysctl's vm.swappiness) memory.move_charge_at_immigrate # set/show controls of moving charges memory.oom_control # oom コントロールの設定をする/設定を表示する memory.numa_stat # numa ノードごとのメモリ消費量を表示する memory.kmem.limit_in_bytes # カーネルメモリのハードリミットを設定する/表示する memory.kmem.usage_in_bytes # 現在のカーネルメモリの割り当てを表示する memory.kmem.failcnt # カーネルメモリの消費量が制限値にヒットした回数を表示する memory.kmem.max_usage_in_bytes # 記録されているカーネルメモリ消費量の最大値を表示する memory.kmem.tcp.limit_in_bytes # TCPバッファメモリのハードリミットを設定する/表示する memory.kmem.tcp.usage_in_bytes # 現在の TCP バッファメモリの割り当てを表示する memory.kmem.tcp.failcnt # TCP バッファの消費量が制限値にヒットした回数を表示する memory.kmem.tcp.max_usage_in_bytes # 記録されているTCPバッファのメモリ消費量の最大値を表示する
1. 歴史
メモリコントローラには長い歴史がある.メモリコントローラに対する RFC は Balbir Singh によって投稿された (http://lwn.net/Articles/206697/).当時,投稿された RFC には,メモリコントローラの実装がいくつかあった.RFC の目標は,メモリの制御に必要な最小限の機能に対するコンセンサスと同意を確立することであった.最初の RSS コントローラは Balbir Singh によって 2007 年の 2 月に投稿された (http://lwn.net/Articles/222762/).Pavel Emelianov が,それ以来 3 つのバージョンの RSS コントローラを投稿している.(http://lkml.org/lkml/2007/3/6/198 , http://lkml.org/lkml/2007/4/9/78 , http://lkml.org/lkml/2007/5/30/244) OLS のリソースマネジメント BOF では,全員がページキャッシュと RSS の両方を扱う事を提案した.他のリクエストとして,ユーザスペースの OOM の扱いを可能にするというものが持ち上がった.現在のメモリコントローラはバージョン6であり,マップされた(RSS)ページとマップされないページキャッシュコントロールの両方が統合されている.(http://lkml.org/lkml/2007/8/17/69)
2. メモリコントロール
メモリは量が制限されて提供されるという意味で他とは違うリソースである.もしタスクが多数のCPUによる処理が必要な場合,タスクは時間,日,月,年といった単位に渡って処理を拡大することが可能である.しかしメモリでは,タスクを実行するためには,同じ物理メモリを再利用する必要がある.
メモリコントローラの実装は段階に分割されている.その段階とは
- メモリコントローラ
- mlock(2) コントローラ
- カーネルユーザメモリのカウントとスラブのコントロール
- ユーザマッピングの長さのコントローラ (user mappings length controller)
メモリコントローラが最初に開発されたコントローラである.
2.1. デザイン
デザインの中心は res_counter と呼ばれるカウンタである.res_counter は,コントローラに関連付けられたプロセスのグループの現在のメモリ使用量と制限を追跡する.それぞれの cgroup には,関連付けられたメモリコントローラ特有のデータ構造 (mem_cgroup) が存在する.
2.2. アカウンティング
+--------------------+ | mem_cgroup | | (res_counter) | +--------------------+ / ^ \ / | \ +---------------+ | +---------------+ | mm_struct | |.... | mm_struct | | | | | | +---------------+ | +---------------+ | + --------------+ | +---------------+ +------+--------+ | page +----------> page_cgroup| | | | | +---------------+ +---------------+ (Figure 1: Hierarchy of Accounting)
- カウントは cgroup ごとに発生する
- それぞれの mm_struct は自分が属する cgroup について知っている
- ページごとに page_cgroup へのポインタを保持している.この page_cgroup も同様に自分が属する cgroup を知っている
カウントは次のように完了する: mem_cgroup_charge_common() が必要なデータ構造がセットアップを行うために呼び出され,チャージされる cgroup が制限を超えているかどうかチェックされる.もし超過している場合,メモリの回収が cgroup で呼び出される.詳細な説明はこの文書の回収のセクションで読むことが可能である.全てがうまくいった場合,page_cgroup と呼ばれるページのメタデータ構造が更新され,page_cgroup は cgroup に自身の LRU を持つ. (*) page_cgroup 構造体はブート/メモリのホットプラグ時に割り当てられる.
2.2.1. アカウンティングの詳細
全てのマップされた匿名ページ (RSS) とキャッシュページ (Page Cache) がカウントされる.回収されない,LRU に入らないある種のページはカウントされない.通常の VM 管理下のページをカウントする.
RSS ページは,それ以前にカウントされていない限りは page_fault 時にカウントされる.ファイルページは,inode (radix-tree) に挿入された時にページキャッシュとしてカウントされる.プロセスのページテーブルへマップされる一方で,重複のカウントは注意深く避ける.
RSS ページは,完全にアンマップされた時にカウントから外れる.ページキャッシュのページは radix-tree から削除された時にカウントから外れる.RSS ページが (kswapd によって) 完全にアンマップされた場合でも,完全に開放されるまではシステム上にスワップキャッシュとして存在し続けるかもしれない.このようなスワップキャッシュもカウントされる.スワップインページは,実際にマップされるまではカウントされない.
注意: カーネルはスワップインの先読みと複数のスワップの読み込みを一度に行う.これは,スワップインページは,ページフォルトを引き起こすタスク以外の他のタスクのページに含まれる可能性があることを意味する.なので,スワップインI/Oの時のカウントを避けるのである.
ページのマイグレーション時,カウントの情報は保持される.
注意: LRU 上のページをカウントするのは,この目的が使用中のページの量をコントロールすることであるからであり,LRU にないページは VM の視点からするとコントロール外であるからである.
2.3. 共有メモリのカウント
共有メモリはファーストタッチアプローチ (first touch approach) の原則でカウントされます.最初にページにタッチした cgroup がページをカウントする.この原則の背後にある原則は,共有メモリを積極的に使う cgroup が,最終的にチャージを得るということです (一旦,チャージされた cgroup からのアンチャージは,メモリが圧迫されている時に起こります).
しかし,Section 8.2 を参照すること: 他の cgroup からタスクが移動する際,もし move_charge_at_immigrate が選択されている場合は,そのページは新しいグループに再チャージされる.
例外: CONFIG_CGROUP_CGROUP_MEMCG_SWAP を使用していない場合.スワップオフが起こり,shmem(tmpfs) のスワップアウトしたページが強制的にメモリに復帰した場合,ページに対するチャージは shmem のユーザでなく,swapoff の呼び出し元に対してカウントされる.
2.4. スワップ拡張 (CONFIG_MEMCG_SWAP)
スワップ拡張はスワップに対するチャージを記録できようにする.スワップインしたページは,可能であれば,元のページアロケータにチャージが戻される.
スワップに対するカウントが行われる場合,以下のファイルが追加される.
- memory.memsw.usage_in_bytes.
- memory.memsw.limit_in_bytes.
memsw は memory+swap を意味する.memory+swap の使用量は memsw.limit_in_bytes で制限される.
例: システムが 4G のスワップを持っていると仮定する.2G のメモリ制限がかかっている タスクが (誤って) 6G のメモリをアロケートすると,全てのスワップが使われるだろう.この場合,memsw.limit_in_bytes=3G の設定を行うことにより,スワップの誤った使用が防げるだろう.memsw 制限を使うことにより,スワップの不足により引き起こされるシステムの OOM を防ぐことが可能になる.
- なぜスワップでなく 'memory+swap'
グローバルな LRU は任意のページをスワップアウト可能である.スワップアウトはメモリからスワップへのカウントの移動を意味する.memory+swap の使用量は変化がない.言い換えれば,グローバルな LRU に影響を与えることなしにスワップの使用量の制限を行いたい場合,memory+swap の制限はOS の観点からスワップのみを制限するよりも良い. - cgroup が memory.memsw.limit_in_bytes にヒットした時何が起こるか
cgroup が memory.memsw.limit_in_bytes にヒットした時,この cgroup ではスワップアウトが使いものにならなくなる.そして,スワップアウトが cgroup のルーチンにより行われなくなり,ファイルキャッシュがドロップする.しかし,前述のように,グローバルな LRU は,システムのメモリ管理の状態が健全になるようにメモリをスワップアウトさせることが可能である.これを cgroup で防ぐことは不可能である.
2.5. 回収
cgroup ごとに,グローバルな VM と同じ構造を持つ cgroup 専用の LRU を管理している.cgroup が制限を超えた場合,cgroup がタッチする新しいページのスペースを開けるために,まずは cgroup からのメモリの回収を試みる.もし回収が成功しなかった場合,OOM ルーチンが呼び出され,cgroup 内のもっとも大きなタスクが選ばれ,kill される.(以下の 10. OOM コントロールを参照)
回収のアルゴリズムは,ページが cgroup ごとの LRU リストから回収のために選択される以外に,cgroup のために変更されているということはない.
注意: 回収は root cgroup では起こらない.root cgroup ではいかなる制限も設定できないからである.
注意2: panic_on_oom が 2 に設定されている時,全システムがパニックとなる.
oom イベント通知が登録されている場合,イベントが設定通りに実行される.(oom コントロールのセクション参照)
2.6. Locking
lock_page_cgroup()/unlock_page_cgroup() は mapping→tree_lock 下で呼ぶべきではない.
他のロックの順は以下の通りである:
PG_locked. mm->page_table_lock zone->lru_lock lock_page_cgroup.
多くの場合,lock_page_cgroup() が呼ばれるだけである.per-zone-per-cgroup LRU (cgroup 専用の LRU) は,zone→lru_lock で守られており,それ自身のロックは持たない.
2.7. カーネルメモリ拡張 (CONFIG_MEMCG_KMEM)
カーネルメモリ拡張で,メモリコントローラはシステムが使うカーネルメモリの量を制限することが可能になる.カーネルメモリは基本的にユーザメモリと異なる.それはスワップアウトできないためである.なので,貴重なリソースを大量に消費することにより,システムを DoS 状態に陥らせることが可能になる.
カーネルメモリは,グループに制限が設定されるまではカウントされない.これにより,既に存在する設定が混乱なく動き続けることが可能となる.制限は cgroup が子グループを持っている場合は設定できない.また,cgroup にタスクが既に設定されている場合も設定できない.これらの状態で設定を行おうとすると -EBUSY が返る.use_hierarchy == 1 で,グループのカウントが行われている時,子グループは制限値に関わらず,自動的にカウントされる.
グループが一度制限されると,制限が消去されたあともカウントは続けられる.メモリの制限それ自体は,-1 を書くことにより削除することが可能である.この場合,kmem はカウントされるが制限はされない状態となる.
カーネルメモリの制限は root cgroup には反映されない.root cgroup の使用量はカウントされるかもしれないし,されないかもしれない.メモリ使用量は memory.kmem.usage_in_bytes に蓄積されるか,別のカウンタに蓄積される (現時点では tcp のみ).メインの kmem カウンタはメインのカウンタにも流し込まれるので,kmem の変化はユーザカウンターからも見ることは可能である.
現時点では,ソフトリミットはカーネルメモリに対しては実装されていない.これは,制限に達した時にスラブの回収のトリガとなるための将来の作業となる.
2.7.1. 現在カウントされるカーネルメモリのリソース
- stack pages: 全プロセスは stack pages をいくらか消費する.これをカウントして,カーネルメモリの消費量が大きい時に新しいプロセスが作成されるのを防ぐ
- slab pages: SLAB or SLUB アロケータによって確保されたページを追跡する.memcg 内部から最初にキャッシュにアクセスがあるたびにそれぞれの kmem_cache のコピーが作られる.この作成はゆっくり行われるので,オブジェクトの中にはキャッシュが作られていてもそれをスキップするものがある.とある slab pages 内のオブジェクトは同じ memcg に属するべきだ.これはタスクがキャッシュによるページアロケーションが行われている最中に違う memcg にマイグレーションされるような時には失敗する.
- socket memory pressure: ソケットプロトコルの中にはメモリ圧迫状態のしきい値を持っているものもある.メモリコントローラはそれらに対してグローバルでコントロールする代わりに cgroup ごとに独立してコントロールを行える.
- tcp memory pressure: TCP プロトコルに対するソケットのメモリ圧迫状態
2.7.3. 一般的なユースケース
“kmem” カウンタはメインのユーザカウンタにも流し込まれるので,カーネルメモリはユーザメモリと完全に独立して制限することはできない.“U” がユーザの制限,“K” がカーネルの制限とすると,以下の 3 つの設定可能な制限の可能性がある.
- U != 0, K = unlimited : 以前から提供されいている普通の memcg の制限メカニズムと同じ.カーネルメモリーは完全に無視される.
- U != 0, K < U : カーネルメモリはユーザメモリのサブセットである.この設定はメモリ cgroup 毎の制限の容量がオーバーコミットであるときに有用である.カーネルメモリのオーバーコミットは全く推奨できない.再利用できないメモリが足りないままになる可能性があるため.この設定では管理者は全てのグループの和がトータルメモリより大きくならないように K を設定すれば,QoS を犠牲にして U を自由に設定できる
- U != 0, K >= U : kmem がユーザカウンターにもカウントされるようになったので,両方の種類の cgroup はメモリ回収のトリガとなる.この設定はメモリの統一的な視点を管理者に与える.そして,これはカーネルメモリをトラッキングしたい人に役に立つ設定である.
3. ユーザインターフェース
- 設定
a. Enable CONFIG_CGROUPS b. Enable CONFIG_RESOURCE_COUNTERS c. Enable CONFIG_MEMCG d. Enable CONFIG_MEMCG_SWAP (to use swap extension) d. Enable CONFIG_MEMCG_KMEM (to use kmem extension)
- cgroup の準備 (なぜ cgroup が必要かは cgroup.txt を参照)
# mount -t tmpfs none /sys/fs/cgroup # mkdir /sys/fs/cgroup/memory # mount -t cgroup none /sys/fs/cgroup/memory -o memory
- 新しいグループを作成し,bash をそこに入れる
# mkdir /sys/fs/cgroup/memory/0 # echo $$ > /sys/fs/cgroup/memory/0/tasks
今,0 cgroup 内にいるので,制限を変えることができる.
# echo 4M > /sys/fs/cgroup/memory/0/memory.limit_in_bytes
注意: キロ,メガ,ギガバイトを表すためにサフィックス (k, K, m, M, g, G) を使用することが可能である.
注意: “-1” を書き込む事で *.limit_in_bytes の制限をリセットする (無制限にする) ことが可能である.
注意: root cgroup にはどのような制限も設定することはできない.
# cat /sys/fs/cgroup/memory/0/memory.limit_in_bytes 4194304
使用量のチェックが可能である:
# cat /sys/fs/cgroup/memory/0/memory.usage_in_bytes 1216512
ファイルに対する書き込みが成功したからと言ってファイルに書いた値を制限に設定するのが成功したと保証されるわけではない.これにはいくつかの理由がある.ページ境界の切り上げやシステムのメモリの利用可能の総量等の要因が関係する.ユーザは書き込みの後に,カーネルがコミットした値を確認するために,ファイルを再度読み込む必要がある.
# echo 1 > memory.limit_in_bytes # cat memory.limit_in_bytes 4096
memory.failcnt フィールドは cgroup が制限を超えた回数を与えてくれる.
memory.stat はアカウンティングの情報を与えてくれる.現時点で,キャッシュ,RSS,アクティブなページ/非アクティブなページの量が見られる.
4. Testing
テストのための機能と実装は memcg_test.txt を参照のこと.
パフォーマンスのテストは重要である.純粋なメモリコントローラのオーバーヘッドを見るために,tmpfs 上でのテストが小さなオーバーヘッドとなるだろう.例: カーネルの make を tmpfs 上で行う.
ページフォールトのスケーラビリティも重要である.並列のページフォールトのテストの計測で,マルチプロセスのテストがマルチスレッドのテストより良くなる可能性がある.これは共有オブジェクト/状態のノイズがあるためである.
しかし,前述の 2 つは極端なテストのシチュエーションである.メモリコントローラの普通のテストをやってみるのは常に役立つ.
4.1. トラブルシューティング
時折,ユーザは cgroup 管理下のアプリケーションが OOM killer によって終了させられるのを見るかもしれない.これにはいくつかの原因がある:
- cgroup の制限が小さすぎる (何か役に立つ事をするには小さすぎる)
- ユーザが匿名メモリやスワップの使用をオフにしているか,小さすぎる
echo 1 > /proc/sys/vm/drop_caches による sync は cgroup 内にキャッシュされたページ (ページキャッシュページ) をある程度取り除いてくれるだろう.
以下の “10. OOM Control” で述べるような OOM_Kill を無効にすることで,何が起こるのかを知り,何が起こるかを見れば役に立つだろう.
4.2. タスクのマイグレーション
ある cgroup から他の cgroup へのタスクのマイグレーションの際,そのチャージはデフォルトでは移動しない.元の cgroup から割り当てられたページは,元の cgroup にチャージされたままである.ページが開放されたり,回収された場合にチャージはなくなる.
タスクのマイグレーションと同時にチャージも移動させることは可能である.“8. Move charges at task migration” を参照の事.
4.3. cgroup の削除
cgroup は rmdir で削除可能である.しかし,4.1 と 4.2 で議論したように,全てのタスクがそこからマイグレーションされた時でも,cgroup はそれに関係するチャージを若干持っているかもしれない.(なぜならチャージはページに対して行われ,タスクに対して行われないため)
統計を root に (use_hierarchy==0の時),もしくは親に (use_hierarchy==1の時)移動すると,子供からアンチャージされたものを除いてチャージに変化はない.(訳注: ここは何が言いたいかわからん)
use_hierarchy については,Section 6 を参照のこと.
5. 様々なインターフェース
5.1. force_empty
memory.force_empty インターフェースは cgroup のメモリ使用量を空にするために提供されている.このインターフェースは cgroup にタスクが存在しない場合に使うことができる.これに対して以下のように書いた場合,
# echo 0 > memory.force_empty
このメモリ cgroup によって追跡されている殆ど全てのページはアンマップされ,開放される.使用中かロックされた状態で開放されないページも存在するかもしれない.このようなページは親に (use_hierarchy==1の時) もしくは root に (use_hierarchy==0の時) に移動し,その cgroup は空になる.
このインターフェースの典型的な使用は rmdir() を呼ぶ前である.なぜなら rmdir() は全てのページを親に移動させる.使われていないページキャッシュも親に移動してしまう可能性がある.もしこれを防ぎたい場合に,force_empty は役に立つだろう.
さらに,memory.kmem.limit_in_bytes が設定されている場合,カーネルページによるチャージも依然存在することに注意が必要である.これは失敗とみなされず,書き込みが依然成功を返すだろう.この場合,memory.kmem.usage_in_bytes == memory.usage_in_bytes であることが期待される.
use_hierarchy については,Section 6 を参照のこと.
5.2. stat file
memory.stat ファイルは以下の統計情報を含む.
# cgroup 毎のローカルなステータス cache - # ページキャッシュメモリのバイト数 rss - # 匿名とスワップキャッシュメモリのバイト数 mapped_file - # マップされたファイルのバイト数 (tmpfs/shmem を含む) pgpgin - # メモリ cgroup に対するチャージイベント(数).チャージイベントは, ページが匿名ページ(RSS)もしくはキャッシュメモリ(Page Cache)のいずれかとして cgroupにカウントされた時に毎回起こる. pgpgout - # メモリ cgroup に対するアンチャージイベント(数). アンチャージイベントはcgroupからカウントが引かれた時に毎回起こる. swap - # スワップの使用量のバイト数 inactive_anon - # inactive LRU リストの匿名メモリとスワップキャッシュメモリのバイト数 active_anon - # active LRU リストの匿名メモリとスワップキャッシュメモリのバイト数 inactive_file - # inactive LRU リストのファイル由来のメモリのバイト数 active_file - # active LRU リストのファイル由来のメモリのバイト数 unevictable - # 回収できないメモリのバイト数 (mlock されてるとか) # 階層構造を考慮したステータス (memory.use_hierarchy の設定を参照のこと) hierarchical_memory_limit - # そのメモリ cgroup 以下の階層構造を考慮したメモリ制限値のバイト数 hierarchical_memsw_limit - # そのメモリ cgroup 以下の階層構造を考慮したメモリ+スワップ制限値のバイト数 total_<counter> - # <counter> の階層構造バージョン.その cgroup 自身の値に加えて全ての階層構造の子供の <counter> の値の和を含む.例えば total_cache # 以下の追加の統計は CONFIG_DEBUG_VM に依存している recent_rotated_anon - VM internal parameter. (see mm/vmscan.c) recent_rotated_file - VM internal parameter. (see mm/vmscan.c) recent_scanned_anon - VM internal parameter. (see mm/vmscan.c) recent_scanned_file - VM internal parameter. (see mm/vmscan.c)
メモ:
recent_rotated は直近の LRU ローテーションの回数を意味する.
recent_scanned は直近の LRU のスキャン回数を意味する.
showing for better debug please see the code for meanings.
注意: 匿名メモリとスワップキャッシュメモリは 'rss' の統計としてリストアップされる.これは実際の 'resident set size' や cgroup で使われている物理メモリの量と混同するべきではない.'rss+file_mapped' はその cgroup 固有のサイズを与えてくれる.(注意: ファイルと shmem は他の cgroup と共有される可能性がある.この場合,file_mapped はそのメモリ cgroup がページキャッシュの所有者である時だけカウントされる)
5.3. swappiness
/proc/sys/vm/swappiness と同様だが,group の階層構造にのみ影響を与える.グローバルの swappiness とは違っており,memcg の knob を 0 に設定すると,swap ストレージが利用可能である場合でも,どんなスワップも本当に防ぐ.これは,もし回収するファイルページがない場合,memcg OOM killer が呼び出されることになるであろうことに注意のこと.
以下の cgroup の swappiness は変更することができない:
- root cgroup (/proc/sys/vm/swappiness を使うこと)
- 階層構造を使用し,配下に他の cgroup を持つ cgroup
- 階層構造を使用し,階層構造の root でない cgroup
5.4. failcnt
メモリ cgroup は memory.failcnt と memory.memsw.failcnt を提供する.この failcnt(==failure count) は使用量カウンタが制限にヒットした回数を表します.メモリ cgroup は制限にヒットしたとき,failcnt の値が増え,その配下のメモリが回収されるだろう.
failcnt は 0 を failcnt ファイルに書き込むことによりリセットできる.
5.5. usage_in_bytes
効率性のために,他のカーネルコンポーネントのように,メモリ cgroup は不要なキャッシュラインのフォールスシェアリングを防ぐためにいくつか最適化がなされている.usage_in_bytes はメソッドの影響を受け,メモリ (またはスワップ) の使用量の正確な値を表示しない.効率的なアクセスのためにあまり正確でない値となる.(もちろん,必要な時,それは同期される)もしより正確なメモリ使用量を知りたい時は,memory.stat 内の RSS+CACHE(+SWAP) の値を使用するべきである.
5.6. numa_stat
これは numa_maps と同様であるが,memcg ごとの規則によって操作される.ページはどの物理的なノードからも割り当て可能であるので,これは memcg 内で numa に関する情報を可視化するのに便利である.ユースケースの一つは,アプリケーションの CPU 割り当ての情報とこの情報を合わせることにより,アプリケーションパフォーマンスを評価することがある.
memcg ごとに,ノードごとの “total”,“file”, “anon”, “unevictable” ページを提供する.memory.numa_stat の出力フォーマットは:
total=<total pages> N0=<node 0 pages> N1=<node 1 pages> ... file=<total file pages> N0=<node 0 pages> N1=<node 1 pages> ... anon=<total anon pages> N0=<node 0 pages> N1=<node 1 pages> ... unevictable=<total anon pages> N0=<node 0 pages> N1=<node 1 pages> ...
ここで total = file + anon + unevictable である.(訳注: unevictable は回収できないページ)
6. 階層構造サポート
メモリコントローラは深い階層構造と階層構造のアカウンティングをサポートしている.階層構造は cgroup ファイルシステム内に適切な cgroup を作成することにより作成される.例として考えられるのは,以下のような cgroup ファイルシステムの改造構造である.
root / | \ / | \ a b c | \ | \ d e
上記の図で,階層構造のアカウンティングが有効になっている場合,e の全てのメモリ使用量は root まで祖先をさかのぼってカウントされる (c と root).階層構造のアカウンティングを有効にするのは memory.use_hierarchy を有効にする.もし,祖先の 1 つが制限を超えた場合,回収アルゴリズムはその祖先とその祖先の子供のタスクから回収を行う.
6.1. 階層構造のアカウンティングと回収を有効にする
デフォルトでは,メモリ cgroup は階層構造機能が無効になっている.このサポートを有効にするには,root cgroup の memory.use_hierarchy ファイルに 1 を書き込む.
# echo 1 > memory.use_hierarchy
この機能を無効にするには
# echo 0 > memory.use_hierarchy
注意1: 有効化/無効化は,cgroup が既に配下に他の cgroup を持つ時,親の cgroup の use_hierarchy が有効になっている場合は失敗する.
注意2: panic_on_oom が 2 に設定されている時,いずれかの cgroup で OOM イベントが発生した場合,全システムがパニックとなる.
7. ソフトリミット
ソフトリミットは以下のようにコマンドを使うことで設定できる.(この例ではソフトリミットを 256MiB と仮定している)
# echo 256M > memory.soft_limit_in_bytes
これを 1G に変えたい場合は,いつでも以下のように実行することが可能である.
# echo 1G > memory.soft_limit_in_bytes
注意1: ソフトリミットは長い時間に渡って効果がある.これはメモリ cgroup 間でバランスを取るためにメモリの回収が呼び出されるからである.
注意2: ソフトリミットはハードリミットよりも常に少ない値で設定することを推奨する.さもないとハードリミットに先に到達してしまう.
8. タスクマイグレーション時のチャージの移動
ユーザはタスクマイグレーションと同時にタスクに関係するチャージを移動することが可能である.これは元の cgroup からタスクのページをアンチャージして,新しい cgroup にチャージするということである.この機能は CONFIG_MMU でない環境ではサポートされない.ページテーブルが欠如しているためである.
8.1. インターフェース
この機能はデフォルトでは無効になっている.目的の memory.move_charge_at_immigrate に書き込むことで有効化,無効化が可能である.
もし有効化したい場合:
# echo (some positive value) > memory.move_charge_at_immigrate
注意: move_charge_at_immigrate のぞれぞれのビットはどの種類のチャージを移動させるべきかという意味を持っている.8.2 を参照.
注意: チャージは mm→owner を移動させたときのみ移動する.言い換えると,スレッドグループのリーダーを移動させたときのみ移動する.
注意: 移動先の cgroup にタスクの充分なスペースが見つけられない場合,メモリを回収することによりスペースを作成しようとする.もし充分なスペースを作ることが出来ない場合は,タスクマイグレーションは失敗する.
注意: 大量のチャージを移動させる場合,数秒かかる場合がある.
8.2. 移動するチャージのタイプ
move_charge_at_immigrate のそれぞれのチャージはどのタイプのチャージを移動させるべきかという意味を持つ.しかしいずれにしても,ページやスワップのアカウントは,タスクの現在の (古い) メモリ cgroup へチャージされた時だけ移動させることができることに注意しなければならない.
bit | どのタイプのチャージが移動するか |
---|---|
0 | ターゲットとなるタスクが使う匿名ページとそれのスワップのチャージ.スワップのチャージを移動できるには,スワップ拡張 (2.4参照) を有効にしなければならない |
1 | ターゲットとなるタスクによってマッピングされたファイルページ (通常のファイル,tmpfsファイル (e.g. ipc 共有メモリ),tmpfs ファイルのスワップ).匿名ページとは違って,タスクがマップした範囲のファイルページ (とスワップ) はページフォールトが起こってない場合でも移動するだろう.すなわち,そのタスクの “RSS” でないかもしれず,同じファイルにマップされた他のタスクの “RSS” であるかもしれない.そして,ページのマップカウントは無視される (ページは page_mapcount(page) > 1 であっても移動する可能性がある). |
(訳注) 0=無効,1=匿名ページとそのスワップ,2=ファイルページ,3=両方指定
8.3. TODO
- チャージを移動させる全ての操作は cgroup_mutex 配下で実行される.これは,mutex を長時間保持する良くない振るまいである.なので技が少し必要かもしれない.
9. メモリのしきい値
メモリ cgroup は cgroup 通知 API (cgroups.txt 参照) を使ったメモリのしきい値を実装している.これにより複数のメモリやmemswのしきい値を登録でき,それが超えた場合に通知を受けとることができる.
しきい値を登録するには,アプリケーションは以下のようにしなければならない.
- eventfd(2) を使って eventfd を作成する
- memory.usage_in_bytes か memory.memsw.usage_in_bytes を open する
- “<event_fd> <fd of memory.usage_in_bytes> <threshold>” のような文字列を cgroup.event_control に書き込む
10. OOM コントロール
memory.oom_control ファイルは OOM 通知や他のコントロールを行うためのファイルである.
メモリ cgroup は cgroup 通知 API (cgroups.txt を参照) を使った OOM 通知を実装している.これにより複数の OOM 通知配送を登録でき,OOM が起こった時に通知を受けとることができる.
通知を登録するには,アプリケーションは以下のようにしなければならない.
- eventfd(2) を使って eventfd を作成する
- memory.oom_control ファイルを open する
- “<event_fd> <fd of memory.oom_control>” というような文字列を cgroup.event_control に書き込む
アプリケーションは OOM が起こると eventfd を通して通知を受けとる.OOM 通知は root cgroup では機能しない.
OOM-killer を無効にするには memory.oom_control ファイルに “1” を書き込む事で可能となる.
#echo 1 > memory.oom_control
この操作は階層構造のトップの cgroup でのみ可能である.OOM-killer が無効化された場合,割り当て可能なメモリを要求したとき,cgroup 配下のタスクは,メモリ cgroup の OOM-waitqueue 内でハングアップするかスリープするだろう.
このようなタスクを実行するには,cgroup の OOM ステータスを以下のように緩和する必要がある.
- 制限を大きくするか,使用量を削減する
- いくつかのタスクを他のグループに移動させ,アカウンティングも移動させる
- いくつかファイルを削減する (tmpfs 上の?)
そして,タスクを停止させ,再度実行する.
以下を見ることで,OOM の現在の状態を見ます.
- oom_kill_disable 0 or 1 (1 の時,oom-killer は無効化されている)
- under_oom 0 or 1 (1 の時,メモリ cgroup は OOM 状態にあり,タスクは停止している可能性がある)
11. TODO
- huge ページのアカウンティングサポート (分離したコントローラとして)
- cgroup ごとのスキャナーが最初に共有されていないページを回収するようにする
- コントローラに共有ページに対するアカウントを教える
- 制限には達していないが,使用量が制限に近づいている時に,バックグラウンドで回収を開始する
Summary
全体的に,メモリコントローラは stable なコントローラであり,コミュニティ内で広く議論とコメントがなされてきた
References
- Singh, Balbir. RFC: Memory Controller, http://lwn.net/Articles/206697/
- Singh, Balbir. Memory Controller (RSS Control),
http://lwn.net/Articles/222762/ - Emelianov, Pavel. Resource controllers based on process cgroups http://lkml.org/lkml/2007/3/6/198
- Emelianov, Pavel. RSS controller based on process cgroups (v2) http://lkml.org/lkml/2007/4/9/78
- Emelianov, Pavel. RSS controller based on process cgroups (v3) http://lkml.org/lkml/2007/5/30/244
- Menage, Paul. Control Groups v10, http://lwn.net/Articles/236032/
- Vaidyanathan, Srinivasan, Control Groups: Pagecache accounting and control subsystem (v3), http://lwn.net/Articles/235534/
- Singh, Balbir. RSS controller v2 test results (lmbench), http://lkml.org/lkml/2007/5/17/232
- Singh, Balbir. RSS controller v2 AIM9 results http://lkml.org/lkml/2007/5/18/1
- Singh, Balbir. Memory controller v6 test results, http://lkml.org/lkml/2007/8/19/36
- Singh, Balbir. Memory controller introduction (v6), http://lkml.org/lkml/2007/8/17/69
- Corbet, Jonathan, Controlling memory use in cgroups, http://lwn.net/Articles/243795/