资讯动态

cleanup_highmap

发布时间:2026/8/24 5:17:27 来源:尧图企业网站定制
cleanup_highmap()是 Linux 内核在 x86_64 架构启动过程中的一个“清道夫”函数。它的核心作用是清理初始内核页表中那些不被需要的映射项确保内核页表只映射实际有效的内存区域。核心目的清理无效的页表映射在内核启动早期head_64.S中的汇编代码会建立一个临时的 2MB 大页映射用于覆盖内核镜像从_text到_end。但这个早期的映射范围是被向上取整到 2MB 边界的因此往往会超出内核映像的实际大小产生一些“空洞”或无效的映射项。如果这些多余的映射项不被清除它们可能会指向物理地址 0或者覆盖了不应被映射为可执行的内存区域这会带来潜在的安全风险也可能导致 TLB快表污染等问题。cleanup_highmap()的任务就是遍历这些由level2_kernel_pgt建立的映射并清除掉那些无效的、地址超出实际范围的条目。工作原理与演进cleanup_highmap()的实现方式经历了一次关键的演进以解决一个与 BRK 区域相关的历史问题。最初的问题在引入.brk段后内核的动态内存区域_brk_end位于_end之后。cleanup_highmap()原本只根据_end来清理映射如果_brk_end和_end跨越了一个 2MB 的页边界那么_end之后到_brk_end之间的部分就可能留下未被清理的“脏”映射项。解决方案通过补丁cleanup_highmap()的逻辑被统一并且被移到了_brk_end确定之后才执行。修正后的函数主要逻辑如下确定有效地址范围它将需要保留的映射限制在_text到_brk_end这个区域内并且_brk_end会被向上对齐到 2MB 边界PMD_SIZE。这样从_text到对齐后的_brk_end之间的映射都会被保留而超出此范围的映射则会被清除。确定结束边界函数的循环还会根据全局变量max_pfn_mapped来计算一个结束的虚拟地址vaddr_end确保它不会越界清理已经被映射的有效内存。max_pfn_mapped记录了内核已经建立映射的物理地址空间的最大页帧号。遍历并清理它会遍历level2_kernel_pgt这个 PMD 页表对每一个 2MB 映射条目进行检查。如果某个条目映射的虚拟地址vaddr位于_text之前或者超过了_brk_end对齐后的地址end就会将该 PMD 条目清除。cleanup_highmap()通过清理掉内核早期页表中不必要的映射保证了页表的简洁性和安全性。它将映射范围精确地限定在_text到_brk_end之间并适应了内核映像布局的演进为后续更精细的内存管理奠定了基础。cleanup_highmap()是 x86_64 架构 Linux 内核启动过程中负责清理初始页表中无效映射的关键函数。它确保内核页表只覆盖实际存在的有效内存区域避免映射超出内核映像范围的内存。核心作用清理初始页表划定精确边界在内核启动初期head_64.S中的汇编代码会建立临时页表使用 2MB 大页映射内核镜像。但由于早期映射的范围会被向上取整到 2MB 边界实际映射范围往往会超出内核镜像_text到_brk_end产生空洞或指向无效物理地址的条目。cleanup_highmap()正是通过遍历 PMD 页表level2_kernel_pgt清除那些虚拟地址不在[_text, _brk_end]范围内的页表项把映射范围精确限定到内核实际占用的区域。函数实现详解该函数定义在arch/x86/mm/init_64.c中。经过关键的演进其最终的实现如下确定有效地址范围起始地址内核代码段起始地址_text。结束地址_brk_end向上对齐到 2MB 边界PMD_SIZE作为允许保留映射的最高地址。循环上界vaddr_end由max_pfn_mapped计算得出确保只处理已建立映射的地址范围。遍历页表并清理遍历对象level2_kernel_pgt页表。遍历范围从__START_KERNEL_map开始直到vaddr_end。清理条件对于每个 PMD 条目如果其映射的虚拟地址vaddr小于_text或者大于已对齐的end即_brk_end则该条目被清除。void __init cleanup_highmap(void) { unsigned long vaddr __START_KERNEL_map; unsigned long vaddr_end __START_KERNEL_map (max_pfn_mapped PAGE_SHIFT); unsigned long end roundup((unsigned long)_brk_end, PMD_SIZE) - 1; pmd_t *pmd level2_kernel_pgt; for (; vaddr PMD_SIZE - 1 vaddr_end; pmd, vaddr PMD_SIZE) { if (pmd_none(*pmd)) continue; /* 清除映射范围之外的 PMD 条目 */ if (vaddr (unsigned long) _text || vaddr end) pmd_clear(pmd); } }关键演进从两个步骤到一个步骤cleanup_highmap()的实现曾经历过一次重要的重构以解决.brk段引入后的问题。演进前的问题早期清理工作分两步进行。第一次在head64.c中执行但此时_brk_end还未确定只能清理到_end。第二次在init_memory_mapping()中尝试清理_brk_end到_end之间的映射。这个流程不仅复杂而且init_memory_mapping()会被多次调用如 NUMA 或内存热插拔时在其中处理初始映射显得不合理。解决方案通过补丁清理工作被统一。现在cleanup_highmap()在reserve_brk()之后调用此时_brk_end已确定一次遍历即可完成所有清理。同时循环条件也改为使用max_pfn_mapped使其更精确。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价