多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

init mop_up_one_pmd

init mop_up_one_pmd mop_up_one_pmd是 x86-64 架构中用于清理单个 PGD 条目及其指向的 PMD 页表的辅助函数位于arch/x86/mm/pgtable.c。它的名字“mop up”清扫形象地描述了它的作用在页表拆除或进程退出时把残留的页表页清理干净。核心作用逐级清理页表这个函数处理的是页表层级中PGD → PMD这一层的清理。当某个 PGD 条目有效时它指向一个 PMD 页表mop_up_one_pmd负责检查 PGD 条目是否有效pgd_val(pgd) ! 0判断该条目是否非空。获取 PMD 页表地址通过pgd_page_vaddr(pgd)从 PGD 条目中提取 PMD 页表的虚拟地址。清除 PGD 条目pgd_clear(pgdp)将该条目清零解除映射关系。半虚拟化通知paravirt_release_pmd()通知 Hypervisor如 Xen释放对应的 PMD 页表页。释放 PMD 页表pmd_free(mm, pmd)将 PMD 页表页归还给页表分配器。更新统计mm_dec_nr_pmds(mm)递减该进程的 PMD 页表计数。调用场景mop_up_one_pmd的典型调用者是pgd_free()或pagetable_pmd_dtor()等函数用于在以下场景中清理页表进程退出当进程终止时其整个页表树需要被拆除mop_up_one_pmd负责清理每个有效的 PGD 条目。exec()切换地址空间当进程执行新程序时旧地址空间的页表需要被回收。页表收缩在某些架构优化中当高层页表变为空时会主动回收以节省内存。设计要点只清理“有效”条目通过pgd_val(pgd) ! 0快速跳过空条目避免不必要的操作。半虚拟化兼容paravirt_release_pmd确保在 Xen 等环境中Hypervisor 的影子页表也能同步更新。统计一致性mm_dec_nr_pmds保持了mm-nr_pmds计数器的准确性这对内存统计和调试很重要。这个函数是页表生命周期管理的“最后一环”确保进程退出时页表占用的内存能被彻底、安全地回收。调用链mop_up_one_pmd的调用关系非常集中它只被pgd_mop_up_pmds这一个函数调用而pgd_mop_up_pmds本身则是在pgd_free路径中被触发的。pgd_free() └── pgd_mop_up_pmds() └── mop_up_one_pmd() // 循环调用各层含义pgd_free()这是进程页表释放的顶层入口在进程退出或exec切换地址空间时被调用。它的实现中明确先调用pgd_mop_up_pmds再调用pgd_dtor。pgd_mop_up_pmds()这是一个循环包装函数它遍历 PGD 页表中所有预分配preallocated的 PMD 条目对每一个非空的条目调用mop_up_one_pmd。注释解释了它的必要性“通常这些 PMD 会由 munmap/exit_mmap 释放但任何预分配、却从未获得对应 VMA 的 PMD 需要手动释放”。mop_up_one_pmd()真正执行清理的函数检查单个 PGD 条目是否有效若有效则释放其指向的 PMD 页表。为什么需要手动清理这段代码的存在是为了处理PGD 预分配机制的“残留物”。在 PAE 或某些半虚拟化Xen配置下内核会在创建新进程的 PGD 时预先分配一批 PMD 页表并填充到 PGD 条目中即使对应的虚拟地址还没有被实际使用。这种预分配是为了避免在页表遍历的敏感路径上动态分配内存。但问题在于如果进程在生命周期内从未实际访问这些预分配的虚拟地址区域那么正常的munmap/exit_mmap路径就不会去释放这些 PMD 页表。因此在pgd_free时pgd_mop_up_pmds会负责把那些“空占着位置”的预分配 PMD 页表清理掉防止内存泄漏。
返回列表