3.7. memory
memory
サブシステムは、cgroup 内のタスクによって使用されるメモリーリソースの自動レポートを生成し、他のタスクによるメモリー使用の上限を設定します。
- memory.stat
- 以下の表に記載した、広範囲なメモリーの統計をレポートします。
表3.2 memory.stat によりレポートされる値 統計 説明 cache
tmpfs
(shmem
) を含むページキャッシュ (バイト単位)rss
tmpfs
(shmem
) を含まない匿名のスワップキャッシュ (バイト単位)mapped_file
tmpfs
(shmem
) を含むメモリーマップドファイルのサイズ (バイト単位)pgpgin
メモリー内へページされたページ数 pgpgout
メモリーからページアウトされたページ数 swap
スワップの使用量 (バイト単位) active_anon
tmpfs
(shmem
) を含む、アクティブな最長時間未使用 (LRU) 一覧上の匿名のスワップキャッシュ (バイト単位)inactive_anon
tmpfs
(shmem
) を含む、非アクティブ LRU 一覧上の匿名のスワップキャッシュ (バイト単位)active_file
アクティブ LRU 一覧にある、ファイルと関連づけされたメモリー (バイト単位) inactive_file
非アクティブ LRU 一覧にある、ファイルに関連付けされたメモリー (バイト) unevictable
再生不可のメモリー (バイト単位) hierarchical_memory_limit
memory
cgroup が含まれる階層のメモリー制限 (バイト単位)hierarchical_memsw_limit
memory
cgroup が含まれる階層のメモリーとスワップの制限 (バイト単位)また、これらのファイルの中で、hierarchical_memory_limit
およびhierarchical_memsw_limit
以外のファイルには、それぞれ、total_
というプレフィックスの付いた対応ファイルがあり、cgroup についてだけでなく、その子グループについてもレポートします。たとえば、swap
は cgroup によるスワップの使用量をレポートし、total_swap
は cgroup とその子グループによるスワップの使用量をレポートします。memory.stat
によってレポートされた値を解析する際には、さまざま統計が相互に関連している点に注意してください。active_anon
+inactive_anon
= 匿名メモリー +tmpfs
のファイルキャッシュ + スワップキャッシュしたがって、active_anon
+inactive_anon
≠rss
となります。これは、rss
にtmpfs
が含まれないのが理由です。active_file
+inactive_file
= cache - size oftmpfs
- memory.usage_in_bytes
- cgroup 内のプロセスによる現在のメモリー総使用量をレポートします (バイト単位)。
- memory.memsw.usage_in_bytes
- cgroup 内のプロセスによる現在のメモリー使用量と使用済みスワップ領域の和をレポートします (バイト単位)。
- memory.max_usage_in_bytes
- cgroup 内のプロセスによるメモリー最大使用量をレポートします (バイト単位)。
- memory.memsw.max_usage_in_bytes
- cgroup 内のプロセスによるスワップメモリー最大使用量と使用済みスワップ領域をレポートします (バイト単位)。
- memory.limit_in_bytes
- ユーザーメモリーの最大値 (ファイルキャッシュを含む) を設定します。単位が指定されていない場合、その値はバイト単位と解釈されますが、より大きな単位を示すサフィックスを使用することが可能です (キロバイトには
k
またはK
、メガバイトにはm
またはM
、ギガバイトにはg
またはG
)。root cgroup を制限するのには、memory.limit_in_bytes
は使用できません。値を適用できるのは、下位階層のグループに対してのみです。memory.limit_in_bytes
に-1
と書き込み、現行の制限値を削除します。 - memory.memsw.limit_in_bytes
- メモリーとスワップ使用量の合計の最大値を設定します。単位が指定されていない場合、その値はバイト単位と解釈されますが、より大きな単位を示すサフィックスを使用することが可能です (キロバイトには
k
またはK
、メガバイトにはm
またはM
、ギガバイトにはg
またはG
)。root cgroup を制限するのに、memory.memsw.limit_in_bytes
は使用できません。値を適用できるのは、下位階層のグループに対してのみです。memory.memsw.limit_in_bytes
に-1
と書き込み、現行の制限値を削除します。重要
memory.limit_in_bytes
パラメーターは、memory.memsw.limit_in_bytes
を設定する前に 設定しておくことが重要となります。逆の順序で設定を試みると、エラーが発生します。これは、memory.memsw.limit_in_bytes
を使用できるようになるのが、(memory.limit_in_bytes
で事前に設定されている) メモリー制限をすべて使い切った後のみであるためです。次の例を検討してください: 特定の cgroup に対してmemory.limit_in_bytes = 2G
とmemory.memsw.limit_in_bytes = 4G
と設定すると、cgroup 内のプロセスが 2 GB のメモリーを割り当てることが可能となり、それを使い果たすと、さらに 2 GB のスワップのみを割り当てます。memory.memsw.limit_in_bytes
パラメーターはメモリーとスワップの合計を示しています。memory.memsw.limit_in_bytes
パラメーターが設定されていない cgroup 内のプロセスは、(設定されているメモリーの上限を消費した後に) 使用可能なスワップをすべて使い果たしてしまい、空きスワップがなくなるために Out Of Memory (OOM) の状態を引き起こす可能性があります。また、/etc/cgconfig.conf
ファイルでmemory.limit_in_bytes
とmemory.memsw.limit_in_bytes
のパラメーターを設定する順序も重要です。この設定の正しい例は以下のとおりです。memory { memory.limit_in_bytes = 1G; memory.memsw.limit_in_bytes = 1G; }
- memory.failcnt
memory.limit_in_bytes
に設定されているメモリーの上限値に達した回数をレポートします。- memory.memsw.failcnt
memory.memsw.limit_in_bytes
に設定されているメモリーとスワップ領域の合計が上限に達した回数をレポートします。- memory.force_empty
0
に設定されている場合には、cgroup 内のタスクによって使用される全ページのメモリーを空にします。このインターフェイスは、cgroup がタスクを持たない時にのみ使用できます。メモリーを解放できない場合は、可能ならば 親 cgroup に移動されます。cgroup を削除する前には、memory.force_empty
を使用して、未使用のページキャッシュが親 cgroup に移動されないようにしてください。- memory.swappiness
- ページキャッシュからページを再生する代わりに、カーネルがこの cgroup 内のタスクで使用されるプロセスメモリーをスワップアウトする傾向を設定します。これはシステム全体用に
/proc/sys/vm/swappiness
内に設定されているのと同じ傾向で、同じ方法で算出されます。デフォルト値は60
です。これより低い値を設定すると、カーネルがプロセスメモリーをスワップアウトする傾向が低減します。また100
以上に設定すると、カーネルはこの cgroup 内のプロセスのアドレス領域となる部分のページをスワップアウトできるようになります。0
の値に設定しても、プロセスメモリーがスワップアウトされるのを防ぐことはできない点に注意してください。グローバル仮想メモリー管理ロジックは、cgroup の値を読み取らないため、システムメモリーが不足した場合に、依然としてスワップアウトが発生する可能性があります。ページを完全にロックするには、cgroup の代わりにmlock()
を使用してください。以下にあげるグループの swappiness は変更できません。/proc/sys/vm/swappiness
に設定された swappiness を使用している root cgroup- 配下に子グループがある cgroup
- memory.use_hierarchy
- cgroup の階層全体にわたって、メモリー使用量を算出すべきかどうかを指定するフラグ (
0
または1
) が含まれます。有効 (1
) となっている場合、メモリーサブシステムはメモリーの上限を超過しているプロセスとその子プロセスからメモリーを再生します。デフォルト (0
) では、サブシステムはタスクの子からメモリーを再生しません。 - memory.oom_control
- cgroup に対して Out of Memory Killer を有効化/無効化するフラグ (
0
または1
) が含まれています。これを有効にすると (0
)、許容量を超えるメモリーを使用しようとするタスクは OOM Killer によって即時に強制終了されます。OOM Killer は、memory
サブシステムを使用するすべての cgroup でデフォルトで有効になっています。これを無効にするには、memory.oom_control
ファイルに1
と記載します。~]#
echo 1 > /cgroup/memory/lab1/memory.oom_control
OOM Killer が無効になると、許容量を超えるメモリーを使用しようとするタスクは、追加のメモリーが解放されるまで一時停止されます。memory.oom_control
ファイルは、現在の cgroup の OOM ステータスもunder_oom
エントリにレポートします。cgroup がメモリー不足の状態で、その cgroup 内のタスクが一時停止されている場合には、under_oom
エントリで値が1
とレポートされます。memory.oom_control
ファイルは、通知 API を使用して OOM 状態の発生をレポートすることができます。詳しくは、「通知 API の使用」 および 例3.3「OOM の制御と通知」 を参照してください。
3.7.1. 使用例
例3.3 OOM の制御と通知
以下の例は、cgroup 内のタスクが許容量を超えるメモリーの使用を試みた場合に OOM Killer がどのように対応し、通知ハンドラーが OOM 状態のどのようにレポートするかを示した実例です。
memory
サブシステムを階層に接続し、cgroup を作成します。~]#
mount -t memory -o memory memory /cgroup/memory
~]#mkdir /cgroup/memory/blue
blue
cgroup 内のタスクが使用できるメモリーを 100 MB に設定します。~]#
echo 104857600 > memory.limit_in_bytes
blue
ディレクトリに移動して、OOM Killer が有効になっていることを確認します。~]#
cd /cgroup/memory/blue
blue]#cat memory.oom_control
oom_kill_disable 0 under_oom 0- 現在のシェルプロセスを
blue
cgroup のtasks
ファイルに移動し、このシェルで起動したその他すべてのプロセスが自動的にblue
cgroup に移動するようにします。blue]#
echo $$ > tasks
- ステップ 2 で設定した上限を超える大容量のメモリーを割り当てようとするテストプログラムを起動します。
blue
cgroup の空きメモリーがなくなるとすぐに OOM Killer がテストプログラムを強制終了し、標準出力にKilled
をレポートします。blue]#
~/mem-hog
Killed以下は、このようなテストプログラムの一例です。[5]#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define KB (1024) #define MB (1024 * KB) #define GB (1024 * MB) int main(int argc, char *argv[]) { char *p; again: while ((p = (char *)malloc(GB))) memset(p, 0, GB); while ((p = (char *)malloc(MB))) memset(p, 0, MB); while ((p = (char *)malloc(KB))) memset(p, 0, KB); sleep(1); goto again; return 0; }
- OOM Killer を無効にし、テストプログラムを再度実行します。今回は、テストプログラムが一時停止の状態のままとなり、追加のメモリーが解放されるのを待機します。
blue]#
echo 1 > memory.oom_control
blue]#~/mem-hog
- テストプログラムが一時停止されている間は、cgroup の
under_oom
状態が変わり、空きメモリーが不足していることを示している点に注意してください。~]#
cat /cgroup/memory/blue/memory.oom_control
oom_kill_disable 1 under_oom 1OOM Killer を再度有効にすると、テストプログラムは即時に強制終了されます。 - すべての OOM 状態についての通知を受信するためには、「通知 API の使用」 に記載したようにプログラムを作成してください。以下はその一例です[6]。
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/eventfd.h> #include <errno.h> #include <string.h> #include <stdio.h> #include <stdlib.h> static inline void die(const char *msg) { fprintf(stderr, "error: %s: %s(%d)\n", msg, strerror(errno), errno); exit(EXIT_FAILURE); } static inline void usage(void) { fprintf(stderr, "usage: oom_eventfd_test <cgroup.event_control> <memory.oom_control>\n"); exit(EXIT_FAILURE); } #define BUFSIZE 256 int main(int argc, char *argv[]) { char buf[BUFSIZE]; int efd, cfd, ofd, rb, wb; uint64_t u; if (argc != 3) usage(); if ((efd = eventfd(0, 0)) == -1) die("eventfd"); if ((cfd = open(argv[1], O_WRONLY)) == -1) die("cgroup.event_control"); if ((ofd = open(argv[2], O_RDONLY)) == -1) die("memory.oom_control"); if ((wb = snprintf(buf, BUFSIZE, "%d %d", efd, ofd)) >= BUFSIZE) die("buffer too small"); if (write(cfd, buf, wb) == -1) die("write cgroup.event_control"); if (close(cfd) == -1) die("close cgroup.event_control"); for (;;) { if (read(efd, &u, sizeof(uint64_t)) != sizeof(uint64_t)) die("read eventfd"); printf("mem_cgroup oom event received\n"); } return 0; }
上記のプログラムはコマンドラインの引数として指定された cgroup 内の OOM 状態を検出し、mem_cgroup oom event received
の文字列を使用して標準出力にレポートします。 blue
cgroup の制御ファイルを引数として指定して、上記の通知ハンドラープログラムを別のコンソールで実行します。~]$
./oom_notification /cgroup/memory/blue/cgroup.event_control /cgroup/memory/blue/memory.oom_control
- 別のコンソールで
mem_hog
テストプログラムを実行し、OOM 状態を発生させてoom_notification
プログラムがそれを標準出力にレポートするのを確認します。blue]#
~/mem-hog