memory vm_normal_page
vm_normal_page是内存管理中的一个核心辅助函数用于判断一个 PTE 是否对应一个“正常”的、由内核内存管理系统跟踪的struct page。如果对应就返回该页面如果不属于正常页面即“特殊映射”则返回NULL。函数定位与作用这个函数的职责非常明确它是 PTE 与struct page之间的一道“过滤器”。正常页面由伙伴系统分配、有引用计数、受内存管理子系统如回收、迁移、RSS 统计管辖的页面。函数返回指向它的struct page指针。特殊映射例如设备 MMIO 区域、vmalloc地址、或者某些不期望被内存管理系统干预的物理页。这些页面没有struct page或者内核不希望你去触碰它。函数返回NULL。实现逻辑两条路径函数的实现根据架构是否支持pte_special位分为两条主要路径。1. 架构支持CONFIG_ARCH_HAS_PTE_SPECIAL时这是更简洁的路径。PTE 中有一个专门的special位来标记非正常映射。if (IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL)) { if (likely(!pte_special(pte))) goto check_pfn; // 快速路径普通 PTE // ... 处理 special 位已置位的 PTE }快速路径如果pte_special未置位说明是普通 PTE直接跳到check_pfn进行 PFN 有效性校验然后返回pfn_to_page(pfn)。特殊位已置位此时会依次检查vma-vm_ops-find_special_page某些驱动自定义的查找逻辑、VM_PFNMAP/VM_MIXEDMAP标志、零页、devmap等最终返回NULL。如果都不匹配会调用print_bad_pte报告异常。2. 架构不支持pte_special时这是一条更复杂的兼容路径内核必须通过vma的属性和 PFN 的数值关系来推断PTE 是否正常。// !CONFIG_ARCH_HAS_PTE_SPECIAL case follows: if (unlikely(vma-vm_flags (VM_PFNMAP|VM_MIXEDMAP))) { if (vma-vm_flags VM_MIXEDMAP) { if (!pfn_valid(pfn)) return NULL; // 混合映射中无 struct page 的返回 NULL goto out; // 有 struct page 的视为正常页面 } else { // VM_PFNMAP unsigned long off; off (addr - vma-vm_start) PAGE_SHIFT; if (pfn vma-vm_pgoff off) return NULL; // 符合线性映射规则是“原始”特殊映射 if (!is_cow_mapping(vma-vm_flags)) return NULL; // 非 COW 映射全部视为特殊 } }这段代码是理解VM_PFNMAP和VM_MIXEDMAP的关键VM_MIXEDMAP允许映射中混合存在正常页面和特殊页面。判断标准是pfn_valid(pfn)。如果该 PFN 在memmap中有对应的struct page就视为正常页面goto out否则返回NULL。VM_PFNMAP用于原始的、完全不受管理的内存映射如设备物理地址。对于 COW 映射如果 PFN 严格满足vma-vm_pgoff offset的线性关系说明这是一个没有被 COW 过的“原始”特殊 PTE返回NULL。如果不满足线性关系或者根本不是 COW 映射那整个映射都不该有正常页面。关键校验与异常处理无论走哪条路径最终都会汇聚到check_pfn标签进行 PFN 的有效性检查check_pfn: if (unlikely(pfn highest_memmap_pfn)) { print_bad_pte(vma, addr, pte, NULL); // 记录并报告坏 PTE return NULL; } out: return pfn_to_page(pfn);这里校验 PFN 是否超出了系统memmap数组的最大范围。如果越界说明 PTE 是损坏的会调用你之前看过的print_bad_pte进行诊断然后返回NULL而不是冒险解引用一个无效的 PFN。总结vm_normal_page是内核内存管理中一个“去伪存真”的函数。它通过pte_special位或vma标志 PFN 计算来区分“受管的正常页面”和“不受管的特殊映射”。其返回的struct page指针或NULL是调用者决定后续行为如增加 RSS 计数、建立反向映射、执行 COW 等的依据。