内存管理-76-缺页异常-1-理论-chatGPT
一、帮忙列出缺页异常各个物理内存分配路径和分配掩码
Linux 6.1 (MTK) 缺页异常的物理内存分配路径与 GFP 掩码
入口流程:
do_page_fault() → handle_mm_fault() → __handle_mm_fault() // 分配页表 + 分发到具体 handler → handle_pte_fault() // 判断具体缺页类型
1. 页表分配
此为所有缺页路径共有。
------------------------------------------------------- 页表级别 函数 GFP 掩码 ------------------------------------------------------- P4D p4d_alloc() GFP_PGTABLE_USER PUD pud_alloc() GFP_PGTABLE_USER PMD pmd_alloc() GFP_PGTABLE_USER PTE pte_alloc_one() GFP_PGTABLE_USER -------------------------------------------------------
分配掩码:
#define GFP_PGTABLE_KERNEL (GFP_KERNEL | __GFP_ZERO) #define GFP_PGTABLE_USER (GFP_PGTABLE_KERNEL | __GFP_ACCOUNT) // 展开 = __GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_ZERO | __GFP_ACCOUNT migratetype = MIGRATE_UNMOVABLE(因为没有 __GFP_MOVABLE)
2. 匿名页缺页
主要是 do_anonymous_page()
(1) 读缺页:使用全局零页(zero_page),不分配物理页。
(2) 写缺页:
page = alloc_zeroed_user_highpage_movable(vma, vmf->address); // 内部调用: alloc_page_vma(GFP_HIGHUSER_MOVABLE | __GFP_CMA, vma, vaddr);
GFP 分配掩码是 GFP_HIGHUSER_MOVABLE,对应迁移类型 __GFP_CMA。前者展开:__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL | __GFP_HIGHMEM | __GFP_MOVABLE | __GFP_SKIP_KASAN_POISON | __GFP_SKIP_KASAN_UNPOISON | __GFP_CMA,只要有 __GFP_MOVABLE 和 __GFP_CMA 就从 MIGRATE_CMA 链表上分配空闲页。
3. 写时复制
即 COW, 对应函数 wp_page_copy()
(1) 源页是零页时:
new_page = alloc_zeroed_user_highpage_movable(vma, vmf->address);
GFP = GFP_HIGHUSER_MOVABLE | __GFP_CMA, migratetype = MIGRATE_MOVABLE + ALLOC_CMA
(2) 源页是真实页时:
new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vmf->address);
GFP = GFP_HIGHUSER_MOVABLE(没有 __GFP_CMA),migratetype = MIGRATE_MOVABLE,不走 CMA
4. 文件映射 COW 缺页
文件映射 COW 缺页:do_cow_fault()
vmf->cow_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vmf->address);
GFP = GFP_HIGHUSER_MOVABLE (没有 __GFP_CMA),migratetype = MIGRATE_MOVABLE
5. 文件映射读缺页
文件映射读缺页,对应函数 do_read_fault() / do_shared_fault()
这两条路径本身不分配数据页,而是通过 __do_fault() → 文件系统 ->fault() → pagecache 分配。pagecache 的分配掩码由 mapping_gfp_mask() 决定,通常为:
// 典型值(ext4 等) mapping_gfp_mask = GFP_HIGHUSER_MOVABLE //有些文件系统会加 __GFP_CMA 或修改
这不属于直接的缺页路径分配,而是文件系统内部的 pagecache readahead 分配。
4. 换入缺页
对应函数 do_swap_page()
同步 I/O 路径(绕过 swap cache,直接读):
folio = vma_alloc_folio(GFP_HIGHUSER_MOVABLE | __GFP_CMA, 0, vma, vmf->address, false);
预读路径(swapin readahead):
page = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE | __GFP_CMA, vmf);
两条路径的 GFP = GFP_HIGHUSER_MOVABLE | __GFP_CMA,migratetype = MIGRATE_MOVABLE + ALLOC_CMA
5. 透明大页 (THP) 缺页
对应函数 do_huge_pmd_anonymous_page()
gfp = vma_thp_gfp_mask(vma); folio = vma_alloc_folio(gfp, HPAGE_PMD_ORDER, vma, haddr, true); vma_thp_gfp_mask() 根据 defrag 策略返回不同掩码:
defrag 策略与掩码:
------------------------------------------------------------------------------------------------------ defrag 策略 GFP 掩码说明 ------------------------------------------------------------------------------------------------------ always GFP_TRANSHUGE (+ __GFP_NORETRY 非 madvise) 同步压缩 defer (kswapd) `GFP_TRANSHUGE_LIGHT __GFP_KSWAPD_RECLAIM` defer+madvise `GFP_TRANSHUGE_LIGHT __GFP_DIRECT_RECLAIM` (madvise) madvise `GFP_TRANSHUGE_LIGHT __GFP_DIRECT_RECLAIM` (madvise时) never GFP_TRANSHUGE_LIGHT 不回收不压缩 ------------------------------------------------------------------------------------------------------
掩码定义:
#define GFP_TRANSHUGE_LIGHT ((GFP_HIGHUSER_MOVABLE | __GFP_COMP | __GFP_NOMEMALLOC | __GFP_NOWARN) & ~__GFP_RECLAIM) #define GFP_TRANSHUGE (GFP_TRANSHUGE_LIGHT | __GFP_DIRECT_RECLAIM)
migratetype = MIGRATE_MOVABLE(注意 THP 默认不带 __GFP_CMA,不会尝试从 CMA 分配)
6. 汇总表
----------------------------------------------------------------------------------------------------------------------------- 缺页场景 分配函数 GFP 掩码 CMA migratetype ----------------------------------------------------------------------------------------------------------------------------- 匿名页(写) alloc_zeroed_user_highpage_movable GFP_HIGHUSER_MOVABLE | __GFP_CMA 是 MOVABLE COW(零页) alloc_zeroed_user_highpage_movable GFP_HIGHUSER_MOVABLE | __GFP_CMA 是 MOVABLE COW(真实页) alloc_page_vma GFP_HIGHUSER_MOVABLE 否 MOVABLE 文件 COW alloc_page_vma GFP_HIGHUSER_MOVABLE 否 MOVABLE 换入(同步IO) vma_alloc_folio GFP_HIGHUSER_MOVABLE | __GFP_CMA 是 MOVABLE 换入(预读) swapin_readahead GFP_HIGHUSER_MOVABLE | __GFP_CMA 是 MOVABLE THP 匿名大页 vma_alloc_folio vma_thp_gfp_mask() 否 MOVABLE PTE 页表 pte_alloc_one GFP_PGTABLE_USER 否 UNMOVABLE PMD 页表 pmd_alloc_one GFP_PGTABLE_USER 否 UNMOVABLE PUD 页表 pud_alloc GFP_PGTABLE_USER 否 UNMOVABLE -----------------------------------------------------------------------------------------------------------------------------
关键观察:
(1) 带 __GFP_CMA 的路径:匿名页分配、零页 COW、swap-in。这些路径会优先尝试从 CMA 区域拿页,CMA 空了才回退到 MOVABLE。
(2) 不带 __GFP_CMA 的路径:真实页 COW、文件 COW、THP。这些路径不会碰 CMA 区域。
(3) 页表分配始终是 UNMOVABLE 类型(不可迁移),不会从 CMA 或 MOVABLE 空闲链表分配。
THP 分配的特殊性:使用 __GFP_NOWARN | __GFP_NOMEMALLOC,且默认不带 __GFP_RECLAIM(即 GFP_TRANSHUGE_LIGHT),分配失败会直接 fallback 到普通 4KB 页,不会阻塞。
posted on 2026-08-14 21:20 Hello-World3 阅读(2) 评论(0) 收藏 举报
浙公网安备 33010602011771号