内存管理-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)    收藏  举报

导航