LWN:要提供更好的 OOM 调试工具!
共 2607字,需浏览 6分钟
·
2022-05-22 17:47
关注了就能看到更多这么棒的文章哦~
Better tools for out-of-memory debugging
By Jonathan Corbet
May 11, 2022
LSFMM
DeepL assisted translation
https://lwn.net/Articles/894546/
Out-of-memory(OOM)的情况是用户、系统管理员以及内核开发人员都很怕面对的。通常人们只知道某处正在使用大量内存,而系统内存已经全用完了,但内核几乎没有提供太多信息来帮助人们弄清内存去哪里了。在 2022 年 Linux 存储、文件系统、内存管理和 BPF 峰会(LSFMM)的内存管理会议上,Kent Overstreet 问道,怎样才能改善 OOM 报告出来的信息,并减少所有参与者的痛苦。
他介绍到,当 OOM 问题发生时,内核会向系统日志写一份报告,但这些报告往往不包括由 slab allocator 管理的内存的相关信息。有时候可能收集了数百份这种报告,但仍然未能确认出问题在哪里。通过他一直在研究的一个新的上报系统(在 LWN 中有简单介绍),这种报告中只包括分配内存最多的十个 slab,这往往就是对他最有用的信息了。对于调试 OOM 问题来说,更重要的是关于内核 shrinker 功能的信息,这些 shrinker 负责在其他地方需要内存的时候从 cache 中回收内存。报告中目前还没有关于 shrinker 正在做什么的相关信息,但是 Overstreet 的新报告机制可以包括对每个 shrinker 的请求,以及 shrinker 实际能够释放多少内存。
他说,这些信息很有用,但这只是一个开始。自 2006 年以来,OOM 报告代码一直没有多少变化,而且已经显得老化过时了,有很大的改进空间。在 OOM 报告方面做了很多工作的 Johannes Weiner 补充说,即使是 2006 年的工作其实也是 "大部分是重构性质的"。大家普遍认为,开发人员需要更好的工具来解决这些内存耗尽的情况。
Overstreet 说,问题的一部分是,以人类可读的方式从内核输出信息并不容易。虽然有各种 "漂亮 print 机制" 可用,大部分是以特殊的 printk() format specifier 描述符的形式。但这些都隐藏在 printk() 代码中,很难找到,漂亮的打印方式最好放在管理它们所打印的数据的代码中。他说,有了正确的基础设施,内核可以有 "成千上万的 pretty printers",输出会变得更好。
关于具体如何改进 OOM 报告,他建议增加速率限制来作为第一步。比如说,一旦都已经打印了十几份报告,那后续的就不可能再有什么价值了。重新组织报告内容,将内核空间和用户空间的内存信息分开则会很有帮助。关于 page allocato 中的碎片情况的信息是必要的,如上所述,需要更多关于 shrinker 的信息。
Weiner 说,过去的报告只打印最耗费内存的任务,而不是所有的任务,但由于某种原因,这个机制被删除了。Michal Hocko 回答说,把最重要的任务挑出来打印出来并不容易,它需要对 task list 加锁,这个操作开销很大。除此之外,关于系统状态的部分信息可能会产生误导,使人很难得到完整了解;如果任务的数量让系统不堪重负了,那么其实就不用关心那几个最主要的内存消耗者了。报告中应该有什么内容是需要取决于当时的情况的。例如,如果一个 GFP_NOWAIT 分配请求最终失败了,那么可能就跟 shrinker (在这种情况下不会被调用)无关了。对于高 order 的内存分配也是如此,在这种情况下,compaction 工作也会失败,开发者需要知道原因。他说,现在报告中的内容是一种妥协,也就是只有那些通常情况下有用的信息。
Overstreet 说,报告的内容必定会是妥协的结果,但有可能创造出比内核现在的更好的报告。Hocko 说,他平时必须处理大量的 OOM 报告,他的感觉是,报告中的数字实在太多了。他经常想做的是检查 slab allocator 所使用的内存相对于 LRU list 上的内存的比例。如果 LRU 内存占主导地位,那么问题几乎肯定在用户空间,报告中如果能明确指出这种情况,就会有所帮助。
Weiner 建议先提供最有用的 summary 信息;其余的信息可以在之后进行。这些报告中数据是否太多则不一定是个问题,尤其是如果这个机制也有速率限制的话。Hocko 反驳说,虽然速率限制在理论上来说是不错的,但实际上并不会奏效。问题是 printk()会很慢,尤其是在使用 serial console 的时候。仅仅把报告中的所有信息打印出来就会使机器瘫痪,而且触发速率控制会需要太长时间。
Ira Weiny 问道,为什么内存可以被分配出去而内核却不知道它去了哪里。问题是,这里并没有用于追踪的基础设施。Overstreet 说,他有一种机制,可以按调用地点来跟踪内存使用情况,效率很高,足以在生产系统中使用。它采用了 dynamic debugging 机制。pr_debug() 宏被修改为在调用位置创建一个静态结构,放在它自己的 ELF 内。然后,这个机制可以用来把 kmalloc()的调用 wrap 起来,从而记住它们来自哪里。
Hocko 问,现有的 tracing 机制不够吗?Overstreet 回答说,他希望有一个始终处于开启状态的东西,这样他就可以在任何时候来查看分配数量。Paul McKenney 建议使用一个 BPF 程序,在地图上存储调用位置的信息。不过,Weiner 回答说,他曾经试过一次,初看起来很容易,做起来要更棘手。有一些情况,例如在中断处理程序中释放内存,就是很难处理的。
会议结束时没有得出什么确切的结论。Overstreet 最后说,他一直在内核中 "发现一些实现得不太好东西",而且开发人员没有以他们本来应该做的方式来看待内存分配。如果幸运的话,将来会有更好的工具来改善这种状况。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~