__get_fault_gfp_mask是一个缺页异常处理路径中的辅助函数,用于为当前触发的缺页异常确定合适的内存分配标志(GFP flags)。它的核心逻辑是:如果 VMA 关联了文件,则使用该文件映射的 GFP 掩码;否则,为特殊映射(如 VDSO)返回一个默认的GFP_KERNEL。
核心实现
该函数的实现非常简洁:
static gfp_t __get_fault_gfp_mask(struct vm_area_struct *vma) { struct file *vm_file = vma->vm_file; if (vm_file) return mapping_gfp_mask(vm_file->f_mapping) | __GFP_FS | __GFP_IO; /* * Special mappings (e.g. VDSO) do not have any file so fake * a default GFP_KERNEL for them. */ return GFP_KERNEL; }关键设计点
1. 文件映射:使用文件系统的 GFP 约束
当 VMA 关联了文件(vma->vm_file非空)时,函数返回mapping_gfp_mask(vm_file->f_mapping) | __GFP_FS | __GFP_IO。
mapping_gfp_mask()获取该文件address_space的 GFP 掩码,通常由文件系统在inode初始化时设置。追加
__GFP_FS | __GFP_IO的原因:mapping_gfp_mask可能包含文件系统施加的约束(如~__GFP_FS用于避免递归调用),但在缺页异常处理路径中,内核通常已经处于一个安全上下文,可以安全地进行文件系统和 I/O 操作来回收内存。因此,函数显式地恢复这两个标志,确保缺页分配有足够的能力触发页面回收。这体现了内核在“尊重文件系统的内存分配约束”和“保证缺页分配有足够回收能力”之间的平衡。
2. 特殊映射:回退到GFP_KERNEL
当 VMA 没有关联文件时(如 VDSO、vsyscall 或某些匿名映射),函数返回GFP_KERNEL。注释明确指出这是为特殊映射“伪造”一个默认值。GFP_KERNEL是内核中最通用的分配标志,允许睡眠、触发 I/O 和文件系统操作,适合在进程上下文的缺页处理中使用。-16
在缺页处理中的角色
该函数在__handle_mm_fault中被调用,用于初始化vm_fault结构体的gfp_mask字段:
struct vm_fault vmf = { .vma = vma, .address = address, ... .gfp_mask = __get_fault_gfp_mask(vma), };这个gfp_mask随后会被传递给缺页处理路径中的各个子函数(如do_anonymous_page、do_fault等),用于指导实际物理页面的分配。例如,在匿名页缺页中,虽然实际使用的是GFP_HIGHUSER_MOVABLE,但vmf->gfp_mask仍然作为基准掩码参与mem_cgroup_try_charge等操作。
设计意图总结
__get_fault_gfp_mask的核心意图是为缺页分配提供一个“上下文感知”的 GFP 掩码:
有文件:继承文件系统的分配约束,但确保具备 FS/IO 回收能力。
无文件:使用通用的
GFP_KERNEL,因为特殊映射通常不涉及复杂的文件系统回收路径。
这种设计避免了在缺页处理路径中硬编码单一的 GFP 标志,而是根据 VMA 的类型动态调整,兼顾了正确性(尊重文件系统约束)和灵活性(确保足够的回收能力)。