我的团队开发了一个在嵌入式Linux上运行的基于多进程C++的复杂系统。由于没有交换分区,因此逐渐增加的内存泄漏可能会导致重大麻烦。(为了讨论起见,让我们假设系统中分配的所有内存都填充了非零数据。
现在,正如这里(简洁地)回答的那样,当操作系统耗尽 RAM 并且没有交换时,它会丢弃干净的页面。据我了解,在这种情况下唯一"干净"的页面是那些包含常量数据和当前/最近从 Linux 环境中执行代码的页面,尤其是我们的可执行文件和共享库,这些页面可能会被无害地丢弃,然后根据需要从文件系统中重新加载。
起初,最近最少使用的页面将是第一个离开的页面,因此这几乎不会被注意到,但随着分配的内存越来越多,回旋余地的数量减少,需要的代码经常被换出然后又被换回去。系统开始无声地、无形地跳动,但我们看到的唯一迹象是系统变得越来越慢,响应速度越来越慢,直到最终内核的杀手介入并做了它的事情。
这种情况不一定需要发生内存泄漏;它可能只是因为我们软件的自然内存需求超过了可用的RAM。这种情况更难捕捉,因为系统不会崩溃,并且由抖动引起的性能影响并不总是立即引起注意,并且可能与性能不佳的其他原因(例如低效的算法)混淆。
我正在寻找一种方法在性能开始受到影响之前明确地捕获和标记此问题;理想情况下,我想监控发生的干净页面丢弃的数量,希望不需要专门重建的内核。然后,我可以建立一些阈值,超过该阈值将引发错误。当然,任何更好的想法也会受到赞赏。
我已经尝试了其他解决方案,例如使用top
监控进程内存使用情况,或者让进程使用mallinfo(3)
自我监管,但这仍然不能捕获所有情况或清楚地回答整体内存使用状态是什么的问题。我看过的另一件事是free
输出中的"free"列,但无论是否发生抖动,它都会显示较低的值。
Alex 的回答通过提到页面错误为我指明了正确的方向,但更具体的答案是主要的页面错误。从 perf_event_open(2) 手册页:
PERF_COUNT_SW_PAGE_FAULTS_MAJ
这将计算主要页面错误的数量。 这些需要磁盘 I/O 来处理。
因此,虽然这些不是我要求的干净页面丢弃,但它们是它们的推论 - 它们指示以前换出的东西何时从磁盘换回。在无交换系统中,唯一可以从磁盘换入的是干净的页面。在我的测试中,我发现这些故障通常很少而且相距甚远,但在内存不足时会突然激增(在我的系统上,它大约是每秒 3 个或更多故障,连续超过 5 秒),并且此指示与系统变慢和响应速度变慢一致。
至于实际查询此统计信息,这在 c 程序的测量页面错误中得到了解答,但我建议从perf_event_open(2)
手册页底部的代码示例开始(请参阅上面的链接),并进行以下更改:
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),读取所有内容并汇总其结果。有关解释原因的线程,请参阅此处。使用 get_nprocs(3) 获取内核数最简单。
我认为您正在寻找的指标是页面错误。作为一个绝对值,它不能告诉你任何事情,因为页面错误是系统操作的正常部分,但作为一个相对值,也许它是有用的:如果你绘制程序在不同内存使用水平上产生的页面错误数量,我敢打赌,当你的程序超过可用RAM并开始这种干净的页面丢弃和加载行为时,将会有一个显着的跳跃。