![/proc/meminfo 中的 Committed_AS 真的是 Linux 中分配的虚拟内存的正确数字吗?它小于 used/RSS](https://linux22.com/image/730242/%2Fproc%2Fmeminfo%20%E4%B8%AD%E7%9A%84%20Committed_AS%20%E7%9C%9F%E7%9A%84%E6%98%AF%20Linux%20%E4%B8%AD%E5%88%86%E9%85%8D%E7%9A%84%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98%E7%9A%84%E6%AD%A3%E7%A1%AE%E6%95%B0%E5%AD%97%E5%90%97%EF%BC%9F%E5%AE%83%E5%B0%8F%E4%BA%8E%20used%2FRSS.png)
我正在收集用于监控 HPC 服务器的数据,并正在讨论分配内存的策略(过量使用或不使用)。我想向用户展示一个数字,即他们的进程(整个机器)请求了多少虚拟内存以及实际使用了多少虚拟内存。
我想使用 MemTotal、MemAvailable 和 Committed_AS 字段从 /proc/meminfo 中获取有趣的值。后者应该显示内核已承诺的内存量,这是完成正在运行的任务真正需要的内存量的最坏情况数字。
但 Committed_AS 显然太小了。它比当前使用的内存还小!观察两个示例系统。一个管理服务器:
# cat /proc/meminfo
MemTotal: 16322624 kB
MemFree: 536520 kB
MemAvailable: 13853216 kB
Buffers: 156 kB
Cached: 9824132 kB
SwapCached: 0 kB
Active: 4854772 kB
Inactive: 5386896 kB
Active(anon): 33468 kB
Inactive(anon): 412616 kB
Active(file): 4821304 kB
Inactive(file): 4974280 kB
Unevictable: 10948 kB
Mlocked: 10948 kB
SwapTotal: 16777212 kB
SwapFree: 16777212 kB
Dirty: 884 kB
Writeback: 0 kB
AnonPages: 428460 kB
Mapped: 53236 kB
Shmem: 26336 kB
Slab: 4144888 kB
SReclaimable: 3863416 kB
SUnreclaim: 281472 kB
KernelStack: 12208 kB
PageTables: 38068 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 24938524 kB
Committed_AS: 1488188 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 317176 kB
VmallocChunk: 34358947836 kB
HardwareCorrupted: 0 kB
AnonHugePages: 90112 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 144924 kB
DirectMap2M: 4988928 kB
DirectMap1G: 13631488 kB
在没有缓存的情况下,大约有 1.5G 已提交,而 2.5G 正在使用。计算节点:
ssh node390 cat /proc/meminfo
MemTotal: 264044768 kB
MemFree: 208603740 kB
MemAvailable: 215043512 kB
Buffers: 15500 kB
Cached: 756664 kB
SwapCached: 0 kB
Active: 44890644 kB
Inactive: 734820 kB
Active(anon): 44853608 kB
Inactive(anon): 645100 kB
Active(file): 37036 kB
Inactive(file): 89720 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 134216700 kB
SwapFree: 134216700 kB
Dirty: 0 kB
Writeback: 140 kB
AnonPages: 44918876 kB
Mapped: 52664 kB
Shmem: 645408 kB
Slab: 7837028 kB
SReclaimable: 7147872 kB
SUnreclaim: 689156 kB
KernelStack: 8192 kB
PageTables: 91528 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 345452512 kB
Committed_AS: 46393904 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 797140 kB
VmallocChunk: 34224733184 kB
HardwareCorrupted: 0 kB
AnonHugePages: 41498624 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 312640 kB
DirectMap2M: 7966720 kB
DirectMap1G: 262144000 kB
大约使用了 47G,而已承诺了 44G。所讨论的系统是 CentOS 7 集群:
uname-a
Linux adm1 3.10.0-862.14.4.el7.x86_64 #1 SMP Wed Sep 26 15:12:11 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
在我使用 vanilla 内核的 Linux 桌面上,我看到更“合理”的数字,已提交 32G,而实际使用量为 15.5G。在 Debian 服务器上,我看到实际使用量为 0.4G,而实际提交量为 1.5G。
有人能给我解释一下吗?如何获取已提交内存的正确数字?这是 CentOS/RHEL 内核中需要报告的错误吗?
更新更多数据并进行系统间比较
我可以访问的各种系统的已使用/已提交内存的列表,以及有关负载类型的说明:
- SLES 11.4(内核 3.0.101-108.71-默认)
- 17.6G/17.4G、交互式多用户 HPC(例如 MATLAB、GIS)
- CentOS 7.4/7.5(内核 3.10.0-862.11.6.el7 或 3.10.0-862.14.4.el7)
- 1.7G/1.3G、管理服务器、集群管理、DHCP、TFTP、rsyslog……
- 8.6G/1.7G,SLURM批处理系统,slurmdbd单独使用7.2G RSS
- 5.1G/0.6G、NFS 服务器(400 个客户端)
- 26.8G/32.6G,16 核 HPC 节点,装载 328 个(需要与用户沟通)GNU R 进程
- 6.5G/8.1G,16 核 HPC 节点,配备 16 个 MPI 进程
- Ubuntu 16.04(内核 4.15.0-33-generic)
- 1.3G/2.2G,6核HPC节点,6线程科学应用(1.1G RSS)
- 19.9G/20.3G,6核HPC节点,6线程科学应用(19G RSS)
- 1.0G/4.4G,6 核登录节点,带 BeeGFS 元数据/管理服务器
- Ubuntu 14.04(内核 3.13.0-161-generic)
- 0.7G/0.3G、HTTP 服务器虚拟机
- 自定义构建(原始内核 4.4.163)
- 0.7克/0.04克,大部分空闲的 Subversion 服务器
- 自定义构建(原始内核 4.14.30)
- 14.2G/31.4G,长时间运行桌面
- Alpine(内核 4.4.68-0-grsec)
- 36.8百万/16.4百万,一些(网络)服务器
- Ubuntu 12.04(内核 3.2.0-89-通用)
- 1.0G/7.1G,部分服务器
- Ubuntu 16.04(内核 4.4.0-112-generic)
- 0.9G/1.9G,部分服务器
- Debian 4.0(内核 2.6.18-6-686,显然是 32 位 x86)
- 1.0G/0.8G,一些可靠的服务器
- Debian 9.5(内核 4.9.0-6)
- 0.4G/1.5G,各种web服务,轻负载,明显
- Debian 9.6(内核 4.9.0-8-amd64)
- 10.9G/17.7G,台式机
- Ubuntu 13.10(内核 3.11.0-26-generic)
- 3.2G/5.4G,老台式机
- Ubuntu 18.04(内核 4.15.0-38-generic)
- 6.4G/18.3G,台式机
SLES 和 CentOS 的 SUnreclaim 相当大……0.5G 到 1G 并不罕见,如果不时不时刷新缓存,则更多。但不足以解释 Committed_AS 中缺少的内存。Ubuntu 机器通常具有低于 100M 的 SUnreclaim。除了 14.04 之外,该机器具有较小的 Committed_AS 和 0.4G SUnreclaim。将内核按顺序排列很棘手,因为 CentOS 的 3.10 内核具有许多 4.x 内核的功能。但似乎在 4.4 和 4.9 之间有一条线,影响了 Committed_AS 奇怪的低值。我的一些同行添加的服务器表明,Committed_AS 也会为较旧的内核提供奇怪的数字。这是否已多次损坏并修复?
有人能证实这一点吗?这只是个 bug 吗?非常在确定 /proc/meminfo 中的值时内核行为不准确,或者是否存在错误(修复)历史记录?
列表中的一些条目是真的很奇怪。一个 slurmdbd 进程的 RSS 是 Committed_AS 的四倍,这肯定是不对的。我很想在这些系统上以相同的工作负载测试 vanilla 内核,但我不能将最有趣的机器从此类游戏的生产中移除。
我猜我的问题的答案是指向内核提交历史中的修复的指针,该修复再次启用了 Committed_AS 中的良好估计。否则,请告诉我 ;-)
更新有关两个进程的 RSS 比 Committed_AS 多
运行 Slurm 数据库守护进程 slurmdbd 实例的批处理服务器以及 slurmctld 就是一个有启发性的示例。它运行时间非常长,并且显示稳定的画面,这两个进程主导着资源使用。
# free -k; for p in $(pgrep slurmctld) $(pgrep slurmdbd) ; do cat /proc/$p/smaps|grep Rss| awk '{ print $2}'; done | (sum=0; while read n; do sum=$((sum+n)); done; echo $sum ); cat /proc/meminfo
total used free shared buff/cache available
Mem: 16321148 5873792 380624 304180 10066732 9958140
Swap: 16777212 1024 16776188
4703676
MemTotal: 16321148 kB
MemFree: 379708 kB
MemAvailable: 9957224 kB
Buffers: 0 kB
Cached: 8865800 kB
SwapCached: 184 kB
Active: 7725080 kB
Inactive: 6475796 kB
Active(anon): 4634460 kB
Inactive(anon): 1007132 kB
Active(file): 3090620 kB
Inactive(file): 5468664 kB
Unevictable: 10952 kB
Mlocked: 10952 kB
SwapTotal: 16777212 kB
SwapFree: 16776188 kB
Dirty: 4 kB
Writeback: 0 kB
AnonPages: 5345868 kB
Mapped: 79092 kB
Shmem: 304180 kB
Slab: 1287396 kB
SReclaimable: 1200932 kB
SUnreclaim: 86464 kB
KernelStack: 5252 kB
PageTables: 19852 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 24937784 kB
Committed_AS: 1964548 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 1814044 kB
DirectMap2M: 14854144 kB
DirectMap1G: 2097152 kB
在这里,您可以看到两个进程的 Rss 总计为 4.5G(slurmdbd 仅为 3.2G)。Rss 与活动的匿名页面有点匹配,但 Committed_AS 小于 2G。通过 /proc 计算所有进程的 Rss 非常接近 AnonPages+shmem(注意:Pss 仅小约 150M)。我不明白 Committed_AS 怎么会小于活动进程的 Rss(总和 Pss)。或者,仅在 meminfo 的上下文中:
Committed_AS (1964548 kB) 怎么会小于 AnonPages (5345868 kB)?这是一个故障稳定的工作负载。这两个进程的寿命极长,几乎是这台机器上唯一发生的事情,而且相当频繁地变动(管理其他节点上的批处理作业)。
答案1
这些盒子没有明显的内存压力。分页也没有(SwapFree
)。第二个盒子占用了约 47 GB,总共 250 GB。200 GB 足够了。
实际上,不断增加工作负载的大小,直到发生以下情况之一:
- 用户(应用程序)响应时间降低
- 页面输出率高于您可接受的水平
- OOM killer 杀死了一些进程
内存计数器之间的关系不直观,在不同的工作负载之间差异很大,可能只有内核开发人员才能真正理解。不要太担心,专注于测量明显的内存压力。
不久前在 linux-mm 列表中对 Comitted_AS 的其他描述强调这是一个估计值:
Committed_AS: An estimate of how much RAM you would need to make a 99.99% guarantee that there never is OOM (out of memory) for this workload. Normally the kernel will overcommit memory. That means, say you do a 1GB malloc, nothing happens, really. Only when you start USING that malloc memory you will get real memory on demand, and just as much as you use. So you sort of take a mortgage and hope the bank doesn't go bust. Other cases might include when you mmap a file that's shared only when you write to it and you get a private copy of that data. While it normally is shared between processes. The Committed_AS is a guesstimate of how much RAM/swap you would need worst-case.
答案2
Committed_AS
以下是另一个纯粹关于低于“预期”的答案:
您说的有趣内容/proc/meminfo
如下:
Active: 4854772 kB
Inactive: 5386896 kB
Active(anon): 33468 kB
Inactive(anon): 412616 kB
Active(file): 4821304 kB
Inactive(file): 4974280 kB
Mlocked: 10948 kB
AnonPages: 428460 kB
Shmem: 26336 kB
Committed_AS: 1488188 kB
(Active
和Inactive
只是稍后(anon)
vs细节的总和,而只是带有标识符的行的总和——我只包含这些行以使其更容易理解。)(file)
AnonPages
(anon)
由于Active(file)
文件支持不会导致任何增加,Committed_AS
因此实际上唯一真正增加您的Committed_AS
值的是内存使用量的AnonPages
+ Shmem
+ + 峰值。这是系统必须能够为当前正在运行的进程提供的内存量(RAM+交换组合),即使所有缓存和缓冲区都刷新到磁盘。Mlocked
Committed_AS
如果某个进程确实(通常在后台malloc()
实现)内核将增加sbrk()
brk()
Committed_AS
),内核将会增加,但不会显示在其他数字中,因为内核实际上并没有保留任何实际 RAM,直到进程实际使用内存为止。 (从技术上讲,内核已指定要用于进程的虚拟内存地址空间范围,但 CPU 的虚拟内存映射指向零填充页面,并带有一个标志,如果进程尝试写入任何内容,则必须动态分配实际内存 - 这允许进程从虚拟地址空间读取零而不会导致 CPU 故障,但将数据写入虚拟分配的内存区域是实际分配内存的操作。)这是非常程序分配的(虚拟)内存比实际使用的内存多,这是很常见的情况,因此这是个很好的功能,但显然会使内存统计信息更难理解。似乎您的系统主要运行的进程不会获取大量未实际使用的内存,因为Committed_AS
与其他值相比,您的内存相当低。
例如我现在的系统目前运行情况如下:
MemTotal: 32570748 kB
Active: 12571828 kB
AnonPages: 7689584 kB
Mlocked: 19788 kB
Shmem: 4481940 kB
Committed_AS: 44949856 kB
注意巨大的Committed_AS
尽管匿名页面总数、锁定内存加上总计约为 12 GB,但我的系统中仍然有大约 45 GB的内存Shmem
。由于我在此系统上运行桌面环境,因此我假设我有许多进程已经执行fork()
。由于我在此系统上运行桌面环境,因此我假设在获取/使用大量 RAM 后执行了许多进程。在这种情况下,分叉进程可以理论上修改所有内存而不进行任何显式内存分配,并且所有这些分叉内存都会向上计算值Committed_AS
。因此,Committed_AS
可能根本无法反映您的实际系统内存使用情况。
总结: Committed_AS
是估计分配的虚拟内存没有任何文件系统备份或必须由实际存储(RAM+交换)支持的最大内存量理论上保持当前正在运行的进程继续运行,如果没有什么在整个系统中分配更多内存。
但是,如果系统正在与外界通信,即使是传入的 IP 数据包也可能会导致使用更多的内存,因此您无法保证未来基于此数字的系统行为。还请注意,堆栈内存始终是动态分配的,因此即使您的任何进程都没有fork()
进行显式内存分配,Committed_AS
当进程使用更多堆栈空间时,您的内存使用量 ( ) 仍可能会增加。
以我的经验来看,Committed_AS
只有与之前的运行进行比较才真正有意义类似的工作量。但是,如果Committed_AS
小于您的,MemTotal
那么您可以肯定,与可用硬件相比,系统的内存压力非常小。
答案3
根据我的经验,Committed_AS
比 更准确MemAvailable
。 尤其是在工作量非常大的情况下,MemAvailable
似乎更像是短时间内的某种平均值,而不是真实值。
话虽如此,我不记得使用过Committed_AS
版本高于 4.15 的内核数据,所以我不知道历史行为是否不同。
和Committed_AS
都是MemAvailable
官方的内核级启发式方法,因此都不应被视为真实事实。
对于我用来运行的工作负载,当Committed_AS
超过实际 RAM 量的 150% 时,我通常会开始遇到性能问题。但是,这显然在很大程度上取决于您的工作负载。如果您有大量泄漏的进程和足够的交换空间,您Committed_AS
可能会继续攀升而不会出现性能问题,因为进程会泄漏 RAM,并且内核会将泄漏的 RAM 区域交换到交换空间。请注意,在这种情况下,Committed_AS
最终可能会远高于总 RAM + 交换空间而不会出现任何问题。
除非您运行的是硬实时系统,否则我不会禁用内存过量使用。而且这样的系统可能也不应该使用任何交换。我个人总是将其/proc/sys/vm/overcommit_memory
设置为1
。
如果你可以提供足够的交换空间,通常增加交换空间/proc/sys/vm/watermark_scale_factor
并/proc/sys/vm/watermark_boost_factor
避免由于交换而导致的延迟是有意义的。但是,重要的是要了解Committed_AS
当前已提交的内存(用户模式进程请求的内存,但通常未完全使用)以及具有 RAM+交换覆盖,可以处理所有情况没有进程分配任何新内存除非你运行一些非常奇特的系统,否则多个进程会不断分配新内存,因此你不应该对未来行为系统。如果您的工作负载非常高,那么当前的数字几乎无法反映系统未来的行为。
对于现代系统,我会关注那些可以包含内存管理中所有最高短期峰值的统计数据。我猜想一个制作精良的统计程序会通过监控内核事件/sys/fs/cgroup/memory/cgroup.event_control
并在内存压力最高时收集统计数据。不过,我不知道有哪个统计应用程序真正支持这一点。任何仅在挂钟定义的采样周期内收集数据的统计应用程序都会错过大部分 RAM 使用率的短期峰值。对于数学上正确的样本平均值,挂钟采样周期是必需的,但了解峰值比获得准确的平均值更重要,因为正是这些峰值而不是平均值会破坏您的进程/性能。