内存管理-66-默认关闭内核配置汇总
一、CONFIG_SPECULATIVE_PAGE_FAULT
1. 简介
性能考量。
CONFIG_SPECULATIVE_PAGE_FAULT 的核心作用是:在低竞争条件下,尝试无锁处理页面错误,避免长时间持有 mmap_lock,以降低足迹锁竞争和上下文切换开销。
1. 它解决的问题
传统页面错误处理必须获取 mmap_lock 写锁; 在高并发多线程/多进程场景下,mmap_lock 竞争激烈,导致大量线程阻塞; 锁竞争造成缓存行失效、上下文切换、调度延迟等开销。
2. 工作原理
(1) 快速路径:当条件允许时(数据不在内存中导致的简单缺页),先尝试无锁处理。
(2) 序列号验证:使用轻量级序列号(seqcount)代替重锁,记录 VMA 树的版本。
(3) 推测执行:在不持锁的状态下,推测性地查找 VMA、分配页面、建立映射。
(4) 提交验证:处理完成后,重新获取锁并验证期间是否有 VMA 改变。
(5) 回滚或提交:若验证通过,提交结果,释放锁,页面错误处理完成(避免了长时间持锁); 若验证失败,回滚,重新用传统方式处理。
3. 适用场景
(1) 简单页面错误:缺页只是因为数据不在 RAM(不是权限问题或其他复杂情况), 对应的文件系统操作简单快速.
(2) 无 VMA 结构变化:处理过程中没有其他线程做 mmap/munmap/mprotect 等操作; 序列号验证通过,说明 VMA 树状态未变。
4. 性能收益
降低锁竞争:减少 mmap_lock 被持有的时间; 减少上下文切换:线程不会因为争锁而被阻塞调出; 提高缓存局部性:减少因锁竞争导致的跨核缓存同步; 整体吞吐量提升:特别是在多核、高并发工作负载上有显著收益(可能 10-30% 甚至更多).
5. 限制与开销
不适用场景:复杂页面错误(如 COW、特殊 VMA 标记、KSM 等); VMA 频繁变化时,推测失败率高,反而浪费 CPU.
代码复杂度:引入额外的序列号检查逻辑,维护成本提升.
验证开销:推测失败时需要重做,可能反复处理同一错误; 在单线程或低竞争场景下收益不大(可能甚至因验证开销略差).
6. 内核版本演进
提议阶段:由 Laurent Dufour 等人在 2017-2018 年左右提出; 实验性支持:部分主线版本曾经历包含/移除的反复; 当前状态(截至 6.x 内核):在某些发行版(如 RHEL、CentOS Stream)中被启用, 在标准主线中仍为可选项,需手动配置 CONFIG_SPECULATIVE_PAGE_FAULT=y; 稳定性和覆盖面在持续改进.
7. 与其他优化的关系
vs Lock-Free VMA Lookup:后者是更激进的无锁方案,不再需要序列号验证;
vs RCU-based mmap_lock:另一种降低竞争的思路,通过 RCU 机制替代重锁;
vs numa_balancing 相关优化:都在努力减少页面错误处理的竞争;
8. 实践建议
(1) 何时启用:高并发多线程应用(如 Java 应用服务器、Web 框架); 频繁进行内存访问但 VMA 结构稳定的场景.
(2) 何时禁用:实时系统(不确定的延迟敏感); VMA 频繁变化的应用; 旧内核或稳定性优先考虑.
(3) 观测指标:perf stat -e page-faults 看缺页率; perf lock 看 mmap_lock 竞争情况; cat /proc/vmstat | grep speculative 查看推测成功/失败统计(如果有这类计数器)
9. 总结
CONFIG_SPECULATIVE_PAGE_FAULT 是一个面向高并发多线程场景的性能优化,通过推测性无锁决策 + 事后验证的套路,规避了传统页面错误处理中 mmap_lock 的长期持有问题。它不是通用加速器,而是针对特定工作负载(高并发缺页冲突多)的"双刃剑"优化。
二、CONFIG_ARCH_USES_PG_UNCACHED
1. 简介
作用:让 page flags 里出现 PG_uncached 位。用来标记“这个页被按 uncached 属性映射过”。
在 arm64 5.4 的实际情况:arm64 并没有默认 select 这个能力,所以通常这个选项对应逻辑是关闭的。结果是 PG_uncached 相关接口会被编译成空实现。arm64 一般通过页表属性(PTE/MAIR)处理缓存属性,不依赖这个 page flag。
三、CONFIG_MEMORY_FAILURE
1. 简介
作用:开启硬件内存错误恢复机制(ECC/RAS 场景)。出现坏页时,可把页打上 HWPoison(PG_hwpoison),隔离并避免再次分配使用。尽量“局部失效”而不是整机崩溃,避免给相关进程发 SIGBUS,或迁移/回收受影响页。
总结来说就是:把硬件上报的坏内存页转换成内核可管理的 HWPoison 状态,利用反向映射和页隔离机制把影响限定在最小范围,并通过 SIGBUS 把故障精确传播给受影响进程,从而实现“坏页可控、系统尽量不停机”。
在 arm64 5.4 的实际情况:arm64 在 Kconfig 里支持 ARCH_SUPPORTS_MEMORY_FAILURE,因此可以启用。启用后会连带 memory isolation、RAS 路径一起生效,适合服务器/高可靠场景。服务器场景常启用(可靠性)。
代价:增加少量代码和运行时处理复杂度;只有出现硬件故障时才明显走相关慢路径。
1. 坏页检测与隔离原理:
CONFIG_MEMORY_FAILURE 接收硬件/固件上报的内存错误,把对应 PFN 标记为 poisoned(坏页),尽量隔离并恢复系统运行(而不是直接 panic)。
发现坏页通常有3种路径:CPU/内存控制器 ECC 检测到不可纠错错误(UE); 平台固件通过 APEI/GHES(尤其 arm64 服务器常见)上报物理地址错误; 架构相关异常路径(如 x86 MCE,arm64 SError/SEA 配合 RAS)把 PFN 交给内核。这些路径最终会调用内核 memory-failure 处理入口(核心函数族在 mm/memory-failure.c####)。
内核把故障粗分为两类:可恢复路径(recoverable);致命路径(unrecoverable)。还会区分页当前状态(空闲buddy页、page cache页、匿名页、swap cache页、huge/THP页、内核关键页(页表、内核文本等)),不同状态动作不同。
核心处理流程:根据物理地址定位 PFN -> struct page; 尝试把该页标记为 PG_hwpoison(硬件坏页); 阻止该页再次被分配(隔离/从 buddy 摘除); 如果页被进程映射,执行“反向映射 + 解除映射 + 通知进程”; 必要时向相关进程发送 SIGBUS(BUS_MCEERR_*); 对无法安全恢复的场景,可能升级为 panic 或更强动作(依系统策略)。一句话:先“封存坏页”,再“清理映射影响”,最后“通知受害者”。
坏页检测依赖两个重要机制:
(1) HWPoison 标记机制: PG_hwpoison 是核心状态位。标记成功后:分配器不会再把该页发出去, 热插拔/回收路径把它当特殊页处理, 即使页经历 alloc/free 周期,这个位也会被保留(防止重用).
(2) 反向映射(rmap)清理机制: 如果坏页已映射到用户空间,内核会:找到映射它的 VMA/PTE, 尝试拆映射、回收或迁移(取决于页类型和状态), 对受影响任务发信号,让用户态感知并处理(崩溃、重试、降级).这一步是“系统不立即死机”的关键。
不同页类型行为差异:
(1) 空闲页坏掉: 最容易处理,直接从 buddy 隔离并打 poison.
(2) 文件页(page cache): 可丢弃后从磁盘重读的,恢复机会较大.
(3) 匿名页: 没有文件后备,通常需要杀死持有映射的任务或做更激进处理.
(4) swap cache 页: 结合 swap 条目处理,避免后续读回坏数据.
(5) THP/huge 页: 可能需要 split 或按 huge 粒度处理,路径更复杂.
(6) 内核关键页(页表等): 通常风险高,可能不可恢复.
arm64 场景下的现实路径: 硬件 RAS 发现错误, 固件通过 GHES/APEI 报告物理地址, 内核解析后走 memory_failure 路径, 对应 PFN 被 hwpoison,相关用户进程收到 SIGBUS,系统继续运行。所以你常看到“单进程挂了,但系统没挂”的现象。
配置项关系:
开启 CONFIG_MEMORY_FAILURE 通常还会联动:ARCH_SUPPORTS_MEMORY_FAILURE(架构能力), CONFIG_RAS(错误上报框架), MEMORY_ISOLATION(隔离辅助), 如果这些链路不完整,CONFIG_MEMORY_FAILURE 的效果会打折。
四、CONFIG_IDLE_PAGE_TRACKING
1. 简介
作用:提供“页是否在一段时间内未被访问”的跟踪能力。典型接口是 page idle bitmap(用于容量评估、内存冷热分析、云平台调度优化)。
在 arm64 5.4 的实际情况:该选项依赖 MMU + SYSFS;arm64 满足。且 arm64 是 64 位,因此会额外使用 PG_young/PG_idle 这两个 page flag 位来跟踪状态。
用途:估算真实热内存工作集(WSS)。为内存 cgroup 配额、虚机迁移、节点放置提供依据。
代价:需要扫描/采样,会有一定观测开销,不建议默认高频使用在所有业务上。
五、CONFIG_SPECULATIVE_PAGE_FAULT
1. 简介
CONFIG_SPECULATIVE_PAGE_FAULT 的核心作用是:让缺页异常处理在“可行时”走一条更轻量的快速路径,尽量不阻塞在传统的 mmap 锁竞争上。
在 Linux 5.4 语境里可以这样理解:
传统缺页路径下,正常 page fault 往往要拿 mm 的读锁(常说 mmap_sem/mmap_lock 读锁)去查 VMA、建页表。多线程高并发缺页时,这把锁会成为热点。
开启 CONFIG_SPECULATIVE_PAGE_FAULT 后, 内核会尝试“推测式”处理:先做无锁/弱锁校验式访问(配合序列号一致性检查)。如果检查期间发现 VMA 或映射关系被并发修改,就立刻放弃这次推测并回退到传统加锁路径重试。####
主要收益是在 mmap/mprotect/munmap 变更不频繁、但 page fault 很频繁的场景下,通常能降低锁竞争、提升吞吐和尾延迟。
代价与边界
并发映射变更频繁时,推测失败重试会增多,收益下降,甚至可能不如传统路径;同时内核实现复杂度更高。
关闭后功能正确性不受影响,只是回到完全传统的加锁缺页处理,通常更保守、更稳定,但高并发下可扩展性可能差一些。
补充一句:这个选项在不同 5.4 分支(主线/Android/common/厂商树)状态可能不同,有的分支没有或默认关闭。
六、CONFIG_DEBUG_VM_RB
1. 简介
CONFIG_DEBUG_VM_RB 在 Linux 5.4 里的作用是:打开 VMA 红黑树增强字段的一致性自检,用来尽早发现内存管理子系统里的结构损坏或更新顺序错误。
主要做的事:
(1) 校验 VMA 红黑树基本有序性,检查按地址遍历时是否单调、是否有重叠、前后遍历节点数是否一致。
(2) 校验增强字段 rb_subtree_gap 是否正确, 每个节点的 rb_subtree_gap 都会和“重新计算值”比对,确保 gap 传播逻辑没出错(这对 unmapped area 搜索很关键)。
(3) 在关键路径做 validate_mm/validate_mm_rb 断言, 像 vma 插入、删除、调整后会触发更多一致性检查,帮助定位 __vma_adjust、vma_gap_update、rb erase/insert 等路径中的隐蔽 bug。
(4) 失败时快速暴露问题, 通常通过 VM_BUG_ON/WARN/pr_emerg 等方式直接报错甚至触发崩溃,便于开发阶段定位。
代价与使用建议:
有明显性能开销: 遍历和重算会增加 mmap/munmap/mprotect 等路径成本。
仅建议用于调试内核: 生产环境一般关闭;排查 MM/VMA 红黑树相关问题时再开启最合适。
七、CONFIG_STACK_GROWSUP
1. 简介
CONFIG_STACK_GROWSUP 的作用是决定用户栈采用“向高地址增长”模型,而不是常见的“向低地址增长”。
在 Linux 5.4 里它主要影响这几块:
(1) 栈扩展方向: 开启:栈向上增长(VM_GROWSUP 语义), 关闭:走默认向下增长(VM_GROWSDOWN).
(2) 缺页触发时的扩栈逻辑: 开启时走 expand_upwards() 路径, 关闭时通常走 expand_downwards() 路径, 对应 find_extend_vma() 里也会按配置选择不同分支。
(3) VMA 边界更新方式: 向上增长主要改 vma->vm_end, 向下增长主要改 vma->vm_start,并处理 vm_pgoff 等联动, 这会连带影响 gap 维护、统计计数、anon_vma 区间更新等 MM 元数据流程。
适用架构:该选项主要给少数架构/场景用(例如历史上 PA-RISC、IA64 某些栈模型);大多数常见平台(x86/arm64)通常不是 grows-up 栈。
关闭后使用的是默认的向下增长模型;这也是你在主流 Linux 上最常见的行为。
八、CONFIG_TRANSPARENT_HUGEPAGE
1. 简介
CONFIG_TRANSPARENT_HUGEPAGE 它的作用是启用透明大页 THP。内核在合适条件下,尽量把普通匿名页按更大的页粒度来映射,典型是把一组 4KB 页合并成 2MB 页,而应用本身不需要显式用 hugetlbfs 或专门 API。
主要效果:
(1) 减少页表项数量, 同样大小的内存区域,用大页映射后页表更小,TLB 压力更低。
(2) 降低 TLB miss, CPU 地址翻译缓存能覆盖更大虚拟地址范围,对大内存工作集常有帮助。
主要面向匿名内存,Linux 5.4 里最典型的是匿名映射、进程堆、栈附近的大块匿名内存。khugepaged 还会后台尝试把满足条件的小页折叠成大页。
不是“强制大页”,之所以叫 transparent,就是应用通常无感知。内核会根据 VMA 属性、对齐情况、碎片情况、是否允许 hugepage 等条件决定是否真的用 THP。
关闭后, 系统仍然完全正常,只是匿名内存通常按普通 4KB 页工作,可能少一些性能收益,但行为更保守、碎片和回收处理也更简单。
相关的VMA标志:VM_HUGEPAGE:倾向启用 THP, VM_NOHUGEPAGE:禁止该 VMA 使用 THP。
九、CONFIG_MEM_SOFT_DIRTY
1. 简介
CONFIG_MEM_SOFT_DIRTY 它的作用是启用 soft-dirty 机制,用来跟踪“某个页或某段 VMA 自某个时刻以来是否被用户态写过”。这个机制主要给检查点/恢复、增量内存同步、脏页追踪类功能使用,比如 CRIU。
主要效果:给页提供“软脏”标记能力,用户态可以先清一次 soft-dirty 状态,然后跑一段时间,再查询哪些页被写过。
配合 /proc/PID/clear_refs 和 /proc/PID/pagemap 使用,常见流程是:先清 soft-dirty,进程继续运行,再从 pagemap 或相关接口读取哪些页变脏, VMA 合并时要特殊处理。
源码里有一句话:VM_SOFTDIRTY should not prevent from VMA merging,意思是 soft-dirty 本身不应成为阻止 VMA merge 的条件,否则会平白制造很多碎片化 VMA。
新映射通常会带上 VM_SOFTDIRTY, 这样用户态能区分“旧区域继续存在”与“原区域被拆掉后重新映射了一块新区域”。
关闭后,系统基本功能不受影响,只是 soft-dirty 跟踪能力不存在,CRIU/增量脏页跟踪这类能力会受限或不可用。
posted on 2026-04-22 11:06 Hello-World3 阅读(18) 评论(0) 收藏 举报
浙公网安备 33010602011771号