在嵌入式 Linux 中检测低内存情况

Detecting low memory situations in Embedded Linux

我的团队在嵌入式 Linux 上开发了一个基于 C++ 的复杂多进程系统 运行。由于没有交换分区,逐渐增长的内存泄漏会造成很大的麻烦。 (为了便于讨论,我们假设系统中分配的所有内存都填充了非零数据。)

现在,正如回答(简洁地)here,当操作系统内存不足且没有交换空间时,它会丢弃干净的页面。据我了解,在这种情况下,唯一的 "clean" 页面是那些包含 const 数据和 currently/recently 从 Linux 环境执行代码的页面,尤其是我们的可执行文件和共享库,它们可能会被无害地丢弃然后根据需要从文件系统重新加载。

起初,最近最少使用的页面会最先离开,所以这几乎不会被注意到,但随着越来越多的内存被分配并且回旋空间减少,更经常需要的代码被换出然后返回。系统开始无声无息地抖动,但我们看到的唯一迹象是系统变得越来越慢且响应速度越来越慢,直到最终内核的 oom-killer 介入并完成它的工作。

这种情况不一定需要发生内存泄漏;它的发生可能仅仅是因为我们软件的自然内存需求超过了可用的 RAM。这种情况更难发现,因为系统不会崩溃,并且由抖动引起的性能损失并不总是立即可见,并且可能与其他导致性能不佳的原因(例如低效算法)相混淆。

我正在寻找一种方法来在性能开始受到影响之前明确地捕获并标记此问题; 理想情况下,我想监控发生的干净页面丢弃量,希望不需要特别重建的内核。然后我可以建立一些阈值,超过该阈值将引发错误。当然,任何更好的想法也会受到赞赏。

我已经尝试过其他解决方案,例如使用 top 监控进程内存使用情况,或者使用 mallinfo(3) 让进程自我监管,但这仍然不能捕捉到所有情况或清楚地回答整体内存使用状态是什么的问题。我看过的另一件事是 free 输出中的 "free" 列,但无论是否发生抖动,它都可以显示较低的值。

我认为您正在寻找的指标是页面错误。作为一个绝对值,它不能告诉你任何事情,因为页面错误是系统操作的正常部分,但作为一个相对值,它也许是有用的:如果你绘制你的程序在不同级别的内存使用情况下产生的页面错误的数量,我敢打赌,当您的程序超出可用 RAM 并开始这种干净的页面丢弃和加载行为时,会有一个显着的跳跃。

Alex 的回答通过提及页面错误为我指明了正确的方向,但更具体的答案是 主要页面错误。来自 perf_event_open(2) man page:

 PERF_COUNT_SW_PAGE_FAULTS_MAJ

This counts the number of major page faults. These required disk I/O to handle.

因此,虽然这些不是我要求的干净页面丢弃,但它们是它们的必然结果 - 它们指示先前换出的内容何时从磁盘换回。在无交换系统中,唯一可以从磁盘换入的是干净的页面。在我的测试中,我发现这些故障通常很少见,但当内存不足时突然出现峰值(在我的系统上,连续 5 秒以上每秒出现 3 次或更多次故障),并且此指示与系统变得越来越慢,响应速度越来越慢。

至于实际查询此统计信息,已在 Measure page faults from a c program 中回答,但我建议从 perf_event_open(2) 手册页底部的代码示例开始(请参阅上面的 link)有了这个变化:

pe.type = PERF_TYPE_SOFTWARE;
pe.config = PERF_COUNT_SW_PAGE_FAULTS_MAJ;

假设您想要获得系统范围的统计信息,而不仅仅是与当前进程有关的统计信息,请将实际打开的行更改为:

fd = perf_event_open(&pe, -1, cpu, -1, 0);

这里的 cpu 论点很棘手。在单核单 CPU 系统上只需将其设置为 0。否则您将不得不为每个核打开一个单独的性能计数器(具有单独的 fd),读取它们并总结它们的结果。有关解释原因的线程,请参阅 here. It is easiest to get the number of cores using get_nprocs(3).