内存管理-33-2-GFP分配掩码约束

基于 Linux-6.1.114


一、全局约束

全局约束通过全局变量 gfp_allowed_mask 实现,它是一个全局 GFP 位掩码过滤器,所有页面分配请求的 gfp_mask 在进入分配器前都会与它做 AND 运算,将系统当前不允许使用的 GFP 标志位强制清除。


1. 赋值位置

(1) 初始定义

//mm/internal.h
#define GFP_BOOT_MASK (__GFP_BITS_MASK & ~(__GFP_RECLAIM|__GFP_IO|__GFP_FS))
//mm/page_alloc.c
gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK;

初始状态: 不允许回收(同步/异步),不允许 IO、不允许 FS。作用域是内核启动期间。

(2) 启动完成

/* 根据配置是 1<<28 - 1 或 1<<29 -1,包含全部 ___GFP_X 掩码 */
#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))

start_kernel //init/main.c
    rest_init //init/main.c
        kernel_thread(kernel_init, NULL, CLONE_FS) //创建init线程
            kernel_init //main.c
                kernel_init_freeable //init/main.c
                    gfp_allowed_mask = __GFP_BITS_MASK;

内核启动完成后解除限制,这个解除限制的时间还是挺晚的,init 进程运行后才解除。

(3) Suspend/Resume

hibernation_snapshot //hibernate.c
hibernation_restore //hibernate.c
enter_state //suspend.c
    pm_restrict_gfp_mask //page_alloc.c
        saved_gfp_mask = gfp_allowed_mask; //保存当前值
        gfp_allowed_mask &= ~(__GFP_IO | __GFP_FS); //清除 IO 和 FS 位
    suspend_devices_and_enter //执行休眠,resume后从这里退出
    pm_restore_gfp_mask
        pm_restore_gfp_mask
            gfp_allowed_mask = saved_gfp_mask;
            saved_gfp_mask = 0;

在系统休眠/挂起时限制 IO, 在 suspend/hibernate 路径中调用:设备即将挂起,存储设备将不可用,禁止任何分配请求触发磁盘 IO(writeback、swap out 等)。


2. 使用位置

(1) 主分配入口

__alloc_pages //page_alloc.c
    gfp &= gfp_allowed_mask; //第一步就做全局掩码过滤
    gfp = current_gfp_context(gfp); //然后再处理任务上下文约束

这是所有页面分配的必经之路,确保任何分配请求都不会使用当前系统禁止的 GFP 标志。

(2) 批量分配

__alloc_pages_bulk //page_alloc.c
    gfp &= gfp_allowed_mask;

这是批量分配入口函数,批量分配路径同样做过滤。只有使能 CONFIG_NUMA 才有调用路径。

(3) 存储挂起判断

bool pm_suspended_storage //page_alloc.c
{
    if ((gfp_allowed_mask & (__GFP_IO | __GFP_FS)) == (__GFP_IO | __GFP_FS))
        return false;   //IO 和 FS 都允许 --> 存储正常
    return true;        //IO 或 FS 被禁止 --> 存储已挂起
}

folio_free_swap //mm/swapfile.c
    if (pm_suspended_storage()) //返回false表示先不释放swap空间
        return false;

__alloc_pages_slowpath
    __alloc_pages_may_oom
        if (pm_suspended_storage())
            goto out;

folio_free_swap: 本质是"防御性设计":在存储不可用期间,保留 swap 备份作为数据的"保险",确保即使出现意外的内存回收,数据也不会丢失。等存储Resume后再安全释放。

__alloc_pages_may_oom() 调用:若存储已挂起则跳过 OOM killer, 因为 OOM 杀的进程可能因 IO 阻塞无法退出。


3. 小结

(1) 三个生命阶段

--------------------------------------------------------------------------------------------------------------
阶段            gfp_allowed_mask 的值                含义
--------------------------------------------------------------------------------------------------------------
早期启动        GFP_BOOT_MASK(受限子集)                中断未启用、设备未初始化,不允许 IO/FS/同步异步回收等操作
系统正常运行     __GFP_BITS_MASK(所有位全开)            不做任何额外限制,所有 GFP 标志都允许使用
休眠/挂起期间    ~(__GFP_IO | __GFP_FS)(去除 IO/FS)    设备已挂起,禁止任何 IO 和文件系统操作
--------------------------------------------------------------------------------------------------------------

 

posted on 2026-08-07 17:43  Hello-World3  阅读(4)  评论(0)    收藏  举报

导航