☰
pgtable ___pmd_free_tlb
2026/9/30 7:19:26 网站建设 项目流程

___pmd_free_tlb是 x86 架构中用于在 TLB 批量刷新(mmu_gather)过程中,延迟释放一个 PMD 页表页的底层函数。它的核心特点是将页表页的释放推迟到 TLB 刷新之后,并处理 PAE 模式下的特殊需求。

核心作用:延迟释放与 TLB 协调

当内核拆除一段页表映射时,对应的 PMD 页表页需要被释放。但直接释放是危险的:如果 TLB 中还缓存着指向该 PMD 的条目,释放后可能导致 CPU 访问已释放内存。因此,内核使用mmu_gather机制:

  1. 收集:将要释放的页表页记录到tlb->pages数组中。

  2. 刷新 TLB:在所有收集完成后,执行 TLB 刷新。

  3. 释放:TLB 刷新后,才真正释放这些页表页。

___pmd_free_tlb负责第 1 步——将 PMD 页表页加入待释放列表。

代码逐行解析

void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd) { paravirt_release_pmd(__pa(pmd) >> PAGE_SHIFT);

半虚拟化通知:paravirt_release_pmd是一个半虚拟化钩子。在 Xen 等虚拟化环境中,客户机释放页表页时需要通知 Hypervisor,因为 Hypervisor 需要维护自己的影子页表。__pa(pmd)获取 PMD 的物理地址,>> PAGE_SHIFT转换为页帧号。

#ifdef CONFIG_X86_PAE tlb->need_flush_all = 1; #endif

PAE 特殊处理:在启用 PAE(物理地址扩展)的 32 位系统上,PMD 层级实际上对应的是页目录指针表(PDPT)。修改 PDPT 的顶层条目需要完整的 CR3 重载才能刷新 TLB,因为INVLPG指令对 PDPT 条目的失效效果不可靠。因此,设置need_flush_all = 1,强制后续执行全局 TLB 刷新。

tlb_remove_ptdesc(tlb, virt_to_ptdesc(pmd)); }

加入待释放列表:virt_to_ptdesc(pmd)将 PMD 的虚拟地址转换为struct ptdesc指针(这是内核将页表描述符从struct page中拆分出来的新机制)。tlb_remove_ptdesc将其加入tlb->pages数组,等待 TLB 刷新后统一释放。

在页表释放链条中的位置

这个函数通常由宏pmd_free_tlb调用,而后者在free_pgd_range→free_pud_range→free_pmd_range的递归释放过程中被触发。它确保了 PMD 页表页的释放与 TLB 刷新的正确顺序,避免了“释放后仍被 TLB 引用”的竞态条件。

谁调用___pmd_free_tlb

它由pmd_free_tlb()这个包装函数(或宏)调用。在 x86 架构下,pmd_free_tlb()的典型实现是:

static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd, unsigned long address) { if (mm_pmd_folded(tlb->mm)) return; ___pmd_free_tlb(tlb, pmd); }

pmd_free_tlb()本身则是在free_pmd_range()中被调用的,而free_pmd_range()又是free_pud_range()→free_pgd_range()→free_pgtables()这个递归释放链条的一部分。

完整的调用链条

free_pgtables() // 释放页表的总入口 └── free_pgd_range() └── free_pud_range() └── free_pmd_range() // ← 在这里调用 pmd_free_tlb └── pmd_free_tlb() └── ___pmd_free_tlb()

这个链条的触发场景包括:进程退出(exit_mmap)、munmap、以及mremap等需要拆除页表映射的操作。

___pmd_free_tlb调用谁

在函数内部,它调用了两个关键函数:

  1. paravirt_release_pmd():半虚拟化钩子,通知 Hypervisor(如 Xen)释放对应的 PMD 页表页。

  2. tlb_remove_ptdesc():将 PMD 页表页加入mmu_gather的待释放列表,延迟到 TLB 刷新后再真正释放。

设计要点

___pmd_free_tlb是一个架构相关的底层函数,它的核心职责是协调页表页释放与 TLB 刷新。直接释放页表页是危险的,因为 TLB 中可能还缓存着指向该页的条目。因此,它通过tlb_remove_ptdesc将释放操作“挂起”,等待tlb_finish_mmu()完成 TLB 刷新后再统一释放。

在 PAE 模式下,它还额外设置tlb->need_flush_all = 1,因为修改顶层 PDPT 条目需要完整的 CR3 重载才能确保 TLB 正确失效。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询