ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

init preallocate_vmalloc_pages

init preallocate_vmalloc_pages preallocate_vmalloc_pages是 x86-64 内核在启动阶段用于预分配 vmalloc 区域页表页的初始化函数位于arch/x86/mm/init_64.c。它的核心目的是避免运行时昂贵的页表同步操作。核心问题为什么需要预分配在 x86-64 上vmalloc 区域的页表项被修改时内核必须将这些变更同步到系统中所有进程的页表。这个同步操作会获取pgd_lock并遍历系统中所有页表开销很大。解决方案是在启动时一次性预分配好 vmalloc 区域所需的页表页。这样所有进程的页表在创建时就能直接继承这些预分配的页表页运行时不再需要同步。预分配的层级取决于页表模式预分配的目标是最顶层的、需要同步的页表页5 级页表需要预分配P4D层级。4 级页表需要预分配PUD层级因为 P4D 层被折叠PUD 是实际存在的第二硬件层级。代码逻辑修正后该函数在 2020 年经历了一次重要修复。早期版本假设p4d_offset()/pud_offset()会进行存在性检查但实际上它们不会导致在 PGD 条目不存在时错误地解引用垃圾数据。修正后的逻辑变得简洁而健壮static void __init preallocate_vmalloc_pages(void) { unsigned long addr; const char *lvl; for (addr VMALLOC_START; addr VMALLOC_END; addr ALIGN(addr 1, PGDIR_SIZE)) { pgd_t *pgd pgd_offset_k(addr); p4d_t *p4d; pud_t *pud; lvl p4d; p4d p4d_alloc(init_mm, pgd, addr); if (!p4d) goto failed; if (pgtable_l5_enabled()) continue; lvl pud; pud pud_alloc(init_mm, p4d, addr); if (!pud) goto failed; } return; failed: panic(Failed to pre-allocate %s pages for vmalloc area\n, lvl); }关键点直接调用p4d_alloc()和pud_alloc()由这些函数内部处理 PGD 条目不存在的情况避免了手动检查的疏漏。5 级页表下预分配到 P4D 即可因为 P4D 是实际存在的层级4 级页表下需要继续预分配到 PUD因为 P4D 被折叠PUD 是实际的第二硬件层级。分配失败时直接panic()因为这些页表页必须在启动阶段就位否则后续进程页表会缺失这些映射。调用时机与效果该函数在mem_init()中被调用。执行后init_mm.pgd中 vmalloc 区域的顶层页表页就被预先创建好了。当新进程创建页表时内核会复制init_mm的 PGD 条目这些预分配的页表页因此被所有进程共享彻底消除了运行时对 vmalloc 区域的页表同步需求。这个优化也使得 x86-64 上的vmalloc_fault()路径变得不再必要与 x86-32 不同进一步简化了缺页异常处理。
返回列表