☰
pgtable pte_alloc_one
2026/9/30 2:45:33 网站建设 项目流程

pte_alloc_one是 Linux 内核中用于为一个用户进程分配一个 PTE(页表项)页表页的架构相关函数。它位于 x86 架构的页表管理代码中,是进程页表构建的基础操作之一。

核心作用:分配用户页表页

当进程需要建立新的 4KB 页映射时,如果对应的 PTE 页表页还不存在,内核就会调用pte_alloc_one来分配一个新的页表页。这个页表页将用于存放该虚拟地址范围内所有 4KB 页的 PTE 条目。

函数封装逻辑

pgtable_t pte_alloc_one(struct mm_struct *mm) { return __pte_alloc_one(mm, GFP_PGTABLE_USER); }

这个函数非常简洁,它只是将工作委托给__pte_alloc_one,并传入固定的分配标志GFP_PGTABLE_USER。

  • GFP_PGTABLE_USER:这是专为用户页表页设计的分配标志。它组合了GFP_KERNEL_ACCOUNT(将分配计入进程的内存 cgroup 统计)和__GFP_ZERO(分配后自动清零),并隐含了GFP_KERNEL的基本语义。

__pte_alloc_one的工作

__pte_alloc_one是实际的分配函数,它的核心逻辑是:

  1. 分配页表页:调用alloc_pages()分配一个物理页,用于存放 512 个 PTE 条目。

  2. 清零页表:由于GFP_PGTABLE_USER包含__GFP_ZERO,分配器会返回一个已清零的页面。

  3. 统计与记账:通过pgtable_pte_page_ctor()等函数,将页表页纳入页表统计,并可能进行 cgroup 内存记账。

  4. 返回句柄:返回pgtable_t类型(在 x86 上就是struct page *),供上层代码进一步操作。

与pmd_alloc等的协作

在页表构建流程中,pte_alloc_one通常由pte_alloc()或__pte_alloc()调用。当内核需要为某个地址建立 PTE 映射,而发现 PMD 条目为空时,就会触发这一分配。整个过程是:

pmd_alloc() → __pte_alloc() → pte_alloc_one() → __pte_alloc_one()

pte_alloc_one是架构相关的封装层,它确保了用户页表页的分配带有正确的标志(计入用户内存、自动清零),而将实际的分配细节留给通用的__pte_alloc_one。

pte_alloc_one被调用时,伙伴系统已经启动完成了。

伙伴系统的启动时机

伙伴系统(Buddy System)的初始化,发生在内核启动流程的mem_init()阶段。这个函数会调用memblock_free_all(),将启动早期由 memblock 管理的所有空闲内存,通过__free_pages()系列函数逐页释放并注册到伙伴系统中。

这个过程完成后,伙伴系统才正式“上线”,开始能够处理内存分配请求。

pte_alloc_one的调用时机

pte_alloc_one是用于为用户进程分配 PTE 页表页的架构相关函数。它的调用场景主要有两个:

  1. 用户进程创建时:当fork()创建新进程,或者exec()加载新程序时,内核需要为新进程构建页表。

  2. 缺页异常处理时:当用户进程访问一个尚未建立映射的地址,触发缺页异常,内核在handle_mm_fault()中可能需要分配新的 PTE 页表页。

为什么必然在伙伴系统之后

关键在于,pte_alloc_one内部最终会调用alloc_pages()来获取物理页,而alloc_pages()正是伙伴系统的核心分配接口。

从启动流程来看,用户进程的创建和缺页异常处理,都发生在mem_init()完成之后。此时,memblock已经把内存管理权完全交接给了伙伴系统,after_bootmem标志也被置位。因此,当pte_alloc_one被调用时,伙伴系统早已准备就绪,可以安全地为其分配页表页。

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

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

立即咨询