| RFC-0091:zx_system_get_event ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 建议向 `zx_system_get_event()` 系统调用添加新事件 - `ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY`。 |
| 问题 | |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2021-04-12 |
| 审核日期(年-月-日) | 2021-05-04 |
摘要
此 RFC 建议向 zx_system_get_event() 系统调用添加新事件类型 - ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY。 新的内核事件将在 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 之前发出信号,以便于在 OOM 发生时捕获内存诊断信息。
设计初衷
当系统几乎耗尽可用物理内存以满足内存分配请求时,系统会发出 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 信号。driver_manager 进程会等待此 OOM 事件,并在收到信号时要求 fshost 关闭文件系统,以便设备能够尽可能干净地重启。不过,目前还没有好的机制来捕获此时有意义的内存状态,而这对于找出 OOM 的根本原因至关重要。
仅仅等待 OOM 事件,并在 OOM 发生时尝试写出内存报告是不可行的,因为这会与文件系统关闭发生冲突。因此,此 RFC 建议公开一个新事件 ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY,而不是依赖于 ZX_SYSTEM_EVENT_OUT_OF_MEMORY,该事件将尽可能接近 OOM 发出信号,同时尝试在 OOM 之前提供一些空间,以便能够可靠地写出内存报告。例如,如果系统可用内存级别降至 50MB 时发出 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 信号,则可以在可用内存为 60MB 时发出 ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY 信号。
此外,我们还希望从长远来看,在 OOM 发生时执行有序关闭,这将涉及更高级别的用户空间组件(而非 driver_manager)等待 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 事件,并关闭某些关键组件以及文件系统。这是一个范围更大的项目,届时我们将不得不重新审视 OOM 事件信号和报告代码路径。
因此,此 RFC 中的提案可以看作是一个中间步骤,只需要对现有设计进行最小的更改。memory_monitor 进程已经等待 Normal、Warning 和 Critical 事件,并在需要时生成内存报告,因此可以轻松扩展为等待 Imminent-OOM 事件。如果报告生成由 OOM 发生时的有序关闭处理,则此事件可能会在将来被移除,或者我们可能会决定保留它,如果该报告提供了额外的价值。
设计
内核内存回收系统根据三个内存水位线(Warning、Critical 和 OOM)发出内存压力事件信号。此逻辑可以轻松扩展,以在 Critical 和 OOM 之间包含一个新的 Imminent-OOM 水位线。Imminent-OOM 和 OOM 水位线之间的增量可以通过内核命令行选项进行配置。
为了与也可以通过内核命令行配置的现有内存水位线保持一致,此增量将是 MB 为单位的绝对内存量。这也意味着,单个值可能不适用于所有平台,并且需要进行调整,这也是现有内存水位线所共有的属性。此外,内存报告生成本身也需要一定的内存量,在为此增量选择值时也应考虑到这一点。
与 zx_system_get_event() 支持的其他内核事件一样,需要根作业的句柄。与 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 不同,ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY 不需要 ZX_RIGHT_MANAGE_PROCESS 权限。这使得新事件类似于 ZX_SYSTEM_EVENT_MEMORY_PRESSURE_NORMAL、ZX_SYSTEM_EVENT_MEMORY_PRESSURE_WARNING 和 ZX_SYSTEM_EVENT_MEMORY_PRESSURE_CRITICAL,它们也不需要 ZX_RIGHT_MANAGE_PROCESS 权限。换句话说,Normal、Warning、Critical 和
Imminent-OOM 事件可以使用 RootJobForInspect 协议,而 OOM
事件需要 RootJob 协议。
在内核中实现将只需要对现有内存回收逻辑进行最小的更改。 我们只是向传递给 pmm_init_reclamation() 的水位线列表中添加了另一个水位线。 Imminent-OOM 级别与其他内存压力级别不同的唯一方面是,它不会在内核中触发任何内存回收(分页器支持的内存和可丢弃内存的逐出)。此级别纯粹用作诊断级别;在此级别触发内存回收会改变事件最初旨在捕获的内存状态。因此,此新事件在 fuchsia.memorypressure 协议中没有相应的信号,该协议用于驱动用户空间中的内存回收。
实现
可以在单个 CL 中扩展 zx_system_get_event() 系统调用;现有内核事件的等待程序不会受到此更改的任何影响。新事件的等待程序稍后可以在单独的 CL 中采用该事件。
性能
新事件不会影响现有事件的等待程序的性能。新事件在检索事件句柄和等待事件句柄方面的性能特征也应与现有事件类似。用于发出事件信号的内核性能也将与现有事件类似 - 当可用内存级别发生变化时,PMM 系统会找到最接近的内存水位线,以推断内存压力级别;提案只是添加一个新的水位线,而不更改现有逻辑的其余部分。
工效学设计
无。
向后兼容性
无。
安全注意事项
无。
隐私注意事项
无。
测试
我们将编写 Zircon 核心测试,以检索和等待 ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY。采用该事件后,我们还将编写单元测试来验证客户端行为。
文档
我们需要更新 zx_system_get_event() 的系统调用文档。
我们还将在内核概念文档中添加一个条目,解释各种内存压力级别背后的设计初衷、每个级别下预期的响应,以及它们如何与系统的其余部分相关联。
缺点、替代方案和未知因素
缺点:内存报告可能不准确
由于事件是在 OOM 之前发出信号,因此捕获的内存状态与 OOM 不完全一致。不过,只要与 OOM 水位线的增量足够小(几十 MB),这仍然有助于诊断 OOM。
我们可能会遇到这样的情况:生成了报告,但没有出现 OOM,也就是说,如果设备在 Imminent-OOM 事件后能够以某种方式恢复。即使此类报告没有关联的 OOM 事件,但它仍然是有用的内存数据,因为它仍然指向值得调查的内存使用量增加。
在内存分配峰值快速出现的情况下,事件的等待程序也可能无法生成报告。此类情况仍必须通过在本地重现问题,同时并行收集内存指标来诊断。报告仍然应该能够捕获大多数 OOM 的内存诊断信息,因为非常快速的分配峰值很少见。
替代方案 1:使用现有事件而不是新事件
ZX_SYSTEM_EVENT_OUT_OF_MEMORY 不能用于可靠地写出内存报告。这是因为 OOM 事件用于驱动文件系统关闭,因此我们可能无法及时写出报告。
可以通过将 OOM 信号处理从 driver_manager 移到另一个可以驱动更有序关闭的用户空间组件来更改此交互 - 首先写出报告,然后关闭文件系统。此方法需要进行一些同步,以确保在文件系统关闭之前成功写出报告。它还可能要求我们在发出 OOM 信号后放宽 OOM 水位线和重启延迟间隔,因为我们现在会在发出 OOM 事件信号时执行更多工作。对 OOM 信号处理方式的这种更改与在 OOM 发生时进行有序关闭的更大努力(在“设计初衷”部分中简要介绍)相关联,这是一项范围更大的工作,可能会在将来进行。
在 OOM 之前发出信号的另一个现有事件是 ZX_SYSTEM_EVENT_MEMORY_PRESSURE_CRITICAL,我们确实使用它来写出内存报告。不过,Critical 内存水位线与 OOM 水位线相差足够远,因此这些报告可能会遗漏更接近 OOM 的重要内存状态变化。
替代方案 2:更丰富的 API,用于更多诊断事件
我们可以不公开接近 OOM 的专用事件,而是提供一个替代 API,用户可以在其中指定多个自定义诊断内存级别,以便在这些级别收到通知。 这使得 API 更加灵活,并且将来更容易扩展。 不过,目前还没有具体的用例表明此类 API 会很有用。
可能需要捕获诊断信息的其他内存压力级别是 Warning 和 Critical。对于这些级别,我们可以直接使用发出级别更改信号的内核事件。我们已经对 Critical 级别执行此操作。OOM 级别在这方面很特殊,因为无法在 OOM 发生时准确捕获诊断信息。在理想情况下,我们希望在 OOM 发生时捕获状态。由于这是不可能的,我们需要另一个足够接近 OOM 的信号,以便能够捕获相关状态,但又不能太接近,以免遇到类似的问题而无法写出报告。
在先技术和参考文档
zx_system_get_event
文档介绍了内核内存事件的工作原理。
fuchsia.memorypressure
文档介绍了基于
内核事件构建的用户空间内存压力信号。