A.7. memory
memory
子系统自动生成 cgroup 任务使用内存资源的报告,并限定这些任务所用内存的大小:
- memory.stat
- 报告大范围内存统计,见下表:
表 A.2. memory.stat 报告的值 统计数据 描述 cache
缓存页,包括 tmpfs
(shmem
),单位为字节rss
匿名和 swap 缓存,“不”包括 tmpfs
(shmem
),单位为字节mapped_file
memory-mapped 映射文件大小,包括 tmpfs
(shmem
),单位为字节pgpgin
读入内存的页数 pgpgout
从内存中读出的页数 swap
swap 用量,单位为字节 active_anon
激活的“近期最少使用”(least-recently-used,LRU)列表中的匿名和 swap 缓存,包括 tmpfs
(shmem
),单位为字节inactive_anon
未激活的 LRU 列表中的匿名和 swap 缓存,包括 tmpfs
(shmem
),单位为字节active_file
激活的 LRU 列表中的 file-backed 内存,以字节为单位 inactive_file
未激活 LRU 列表中的 file-backed 内存,以字节为单位 unevictable
无法收回的内存,以字节为单位 hierarchical_memory_limit
包含 memory
cgroup 层级的内存限制,单位为字节hierarchical_memsw_limit
包含 memory
cgroup 层级的内存加 swap 限制,单位为字节另外,这些文件除hierarchical_memory_limit
和hierarchical_memsw_limit
之外,都有一个对应前缀total
,它不仅可在该 cgroup 中报告,还可在其子 cgroup 中报告。例如:swap
报告 cgroup 的 swap 用量,total_swap
报告该 cgroup 及其所有子群组的 swap 用量总和。当您解读memory.stat
报告的数值时,请注意各个统计数据之间的关系:active_anon
+inactive_anon
= 匿名内存 +tmpfs
文件缓存 + swap 缓存因此,active_anon
+inactive_anon
≠rss
,因为rss
不包括tmpfs
。active_file
+inactive_file
= 缓存 -tmpfs
大小
- memory.usage_in_bytes
- 报告 cgroup 中进程当前所用的内存总量(以字节为单位)。
- memory.memsw.usage_in_bytes
- 报告该 cgroup 中进程当前所用的内存量和 swap 空间总和(以字节为单位)。
- memory.max_usage_in_bytes
- 报告 cgroup 中进程所用的最大内存量(以字节为单位)。
- memory.memsw.max_usage_in_bytes
- 报告该 cgroup 中进程的最大内存用量和最大 swap 空间用量(以字节为单位)。
- memory.limit_in_bytes
- 设定用户内存(包括文件缓存)的最大用量。如果没有指定单位,则该数值将被解读为字节。但是可以使用后缀代表更大的单位 ——
k
或者K
代表千字节,m
或者M
代表兆字节 ,g
或者G
代表千兆字节。您不能使用memory.limit_in_bytes
限制 root cgroup;您只能对层级中较低的群组应用这些值。在memory.limit_in_bytes
中写入-1
可以移除全部已有限制。 - memory.memsw.limit_in_bytes
- 设定内存与 swap 用量之和的最大值。如果没有指定单位,则该值将被解读为字节。但是可以使用后缀代表更大的单位 ——
k
或者K
代表千字节,m
或者M
代表兆字节,g
或者G
代表千兆字节。您不能使用memory.memsw.limit_in_bytes
来限制 root cgroup;您只能对层级中较低的群组应用这些值。在memory.memsw.limit_in_bytes
中写入-1
可以删除已有限制。重要
在设定memory.memsw.limit_in_bytes
参数“之前”设定memory.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 中的进程分得 2GB 内存,并且一旦用尽,只能再分得 2GB swap。memory.memsw.limit_in_bytes
参数表示内存和 swap 的总和。没有设置memory.memsw.limit_in_bytes
参数的 cgroup 的进程可以使用全部可用 swap (当限定的内存用尽后),并会因为缺少可用 swap 触发 Out of Memory(内存不足) 状态。/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
- 报告内存和 swap 空间总和达到
memory.memsw.limit_in_bytes
设定的限制值的次数。 - memory.force_empty
- 当设定为
0
时,该 cgroup 中任务所用的所有页面内存都将被清空。这个接口只可在 cgroup 没有任务时使用。如果无法清空内存,请在可能的情况下将其移动到父 cgroup 中。移除 cgroup 前请使用memory.force_empty
参数以免将废弃的页面缓存移动到它的父 cgroup 中。 - memory.swappiness
- 将 kernel 倾向设定为换出该 cgroup 中任务所使用的进程内存,而不是从页高速缓冲中再生页面。这与
/proc/sys/vm/swappiness
为整体系统设定的倾向、计算方法相同。默认值为60
。低于60
会降低 kernel 换出进程内存的倾向;高于0
会增加 kernel 换出进程内存的倾向。高于100
时,kernel 将开始换出作为该 cgroup 中进程地址空间一部分的页面。请注意:值0
不会阻止进程内存被换出;系统内存不足时,换出仍可能发生,因为全局虚拟内存管理逻辑不读取该 cgroup 值。要完全锁定页面,请使用mlock()
而不是 cgroup。您不能更改以下群组的 swappiness:- root cgroup,它使用
/proc/sys/vm/swappiness
设定的 swappiness。 - 有子群组的 cgroup。
- memory.use_hierarchy
- 包含标签(
0
或者1
),它可以设定是否将内存用量计入 cgroup 层级的吞吐量中。如果启用(1
),内存子系统会从超过其内存限制的子进程中再生内存。默认情况下(0
),子系统不从任务的子进程中再生内存。 - memory.oom_control
- 包含标签(
0
或者1
),它可以为 cgroup 启用或者禁用“内存不足”(Out of Memory,OOM) 终止程序。如果启用(0
),尝试消耗超过其允许内存的任务会被 OOM 终止程序立即终止。默认情况下,所有使用memory
子系统的 cgroup 都会启用 OOM 终止程序。要禁用它,请在memory.oom_control
文件中写入1
:~]#
echo 1 > /cgroup/memory/lab1/memory.oom_control
禁用 OOM 杀手程序后,尝试使用超过其允许内存的任务会被暂停,直到有额外内存可用。memory.oom_control
文件也在under_oom
条目下报告当前 cgroup 的 OOM 状态。如果该 cgroup 缺少内存,则会暂停它里面的任务。under_oom
条目报告值为1
。memory.oom_control
文件可以使用 API 通知来报告 OOM 情况的出现。 。
A.7.1. 示例应用
例 A.3. OOM 控制和通知
以下示例将演示当 cgroup 中任务尝试使用超过其允许的内存时, OOM 终止程序的工作过程,以及通知处理程序是如何报告 OOM 状态的:
- 在层级中附加
memory
子系统,并创建一个 cgroup:~]#
mount -t memory -o memory memory /cgroup/memory
~]#mkdir /cgroup/memory/blue
- 将
blue
cgroup 中任务可用的内存量设定为 100MB:~]#
echo 104857600 > memory.limit_in_bytes
- 进入
blue
目录并确定已启用 OOM 终止程序:~]#
cd /cgroup/memory/blue
blue]#cat memory.oom_control
oom_kill_disable 0 under_oom 0 - 将当前 shell 进程移动到
blue
cgroup 的tasks
文件中,以便在这个 shell 中启动的其它所有进程会自动移至blue
cgroup:blue]#
echo $$ > tasks
blue]#
~/mem-hog
Killed以下是测试程序实例 [1]:#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 杀手程序,然后重新运行测试程序。这次该测试程序会暂停并等待额外的内存释放:
blue]#
echo 1 > memory.oom_control
blue]#~/mem-hog
- 虽然测试程序处于暂停状态,但请注意该 cgroup 的
under_oom
状态已更改,表示该 cgroup 缺少可用内存:~]#
cat /cgroup/memory/blue/memory.oom_control
oom_kill_disable 1 under_oom 1重启 OOM 终止程序可以立即终止该测试程序。 - 如要收到关于每一个 OOM 的通知,请创建一个 指定的程序。 例如 [2]:
#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; }
上述程序会探测 OOM 状态(从被命令列指定为参数的 cgroup 中),并使用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