#Linux内存管理#请简述vm_normal_page()函数的作用的实现流程。

vm_normal_page() 函数:作用与实现流程

核心作用

vm_normal_page() 是 Linux 内存管理的关键底层函数,将有效的页表项(PTE)安全转换为对应的物理页描述符(struct page*)。

它不增加引用计数,仅负责从 PTE 中解析出页帧信息,并处理特殊内存映射(如零页、设备内存)。

 

核心任务:当 PTE 已存在(pte_present() == true)时,将 pte_t 映射到 “标准”物理页帧的 struct page*,同时过滤内核特殊页(如零页)。

 

实现流程详解

函数定义如下:

struct page *vm_normal_page(struct vm_area_struct *vma, 

                            unsigned long addr, 

                            pte_t pte);

输入:

 

vma: 虚拟地址所属的 VMA(内存区域描述符)。

addr: 目标虚拟地址(用于检测特殊页)。

pte: 有效的页表项(需满足 pte_present(pte) == true)。

输出:

 

正常页:返回对应的 struct page*。

特殊页:处理零页等场景后返回其 struct page*。

无效页:返回 NULL(如设备私有内存)。

具体流程:

检查 PTE 是否为特殊页:

 

if (pte_special(pte)) // PTE 标记为特殊项?

    return NULL; // 不处理(如设备内存)

处理零页(Zero Page):

if (pte_zero(pte)) { // PTE 指向零页?

    return pte_zero_page(addr); // 返回全局零页的 page*

}

零页优化:所有全零物理页共享单个全局页(ZERO_PAGE(0)),避免重复分配。

提取物理页帧号(PFN):

unsigned long pfn = pte_pfn(pte); // 从 PTE 中提取 PFN

验证 PFN 有效性:

if (unlikely(!pfn_valid(pfn))) { // PFN 超出物理内存范围?

    return NULL;                             

}

过滤无效 PFN(如空洞地址、未映射设备内存)。

处理设备直接映射内存(如 GPU VRAM):

if (is_pci_p2pdma_page(pfn_to_page(pfn))) { // PCI P2PDMA 内存?

    return NULL; // 需专用 API 访问

}

此类内存由设备驱动管理,标准内存路径无法访问。

透明大页(THP)子页处理:

if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {

    if (pmd_trans_unstable(pmd)) { // THP 正在分裂?

        return NULL; // 需上层重试

    }

}

返回标准物理页:

return pfn_to_page(pfn); // 转换 PFN → struct page*

pfn_to_page() 通过全局数组 mem_map[] 定位物理页描述符。

关键特性总结

前置条件严格:

必须保证输入的 PTE 有效(pte_present(pte) == true)。若 PTE 无效(如页面被换出),应由上层(如 follow_page())先触发缺页中断。

 

零页透明处理:

将零页(pte_zero())映射到全局共享的物理零页,减少内存占用。

 

设备内存隔离:

显式拒绝 PCI P2PDMA 等特殊内存的转换(返回 NULL),强制驱动使用专用接口(如 get_dev_pagemap())。

 

无副作用:

不操作页面引用计数,纯查询函数。页面生命期由调用者管理(如 get_user_pages() 负责固定)。

 

透明大页支持:

检测 THP 分裂状态,避免返回中间态的不稳定页。

 

在内存访问流程中的角色

在典型的用户空间内存访问路径中:

get_user_pages()

    └─> follow_page_mask() // 遍历页表获取 PTE

        └─> vm_normal_page(pte) // 转换 PTE → struct page*

follow_page_mask() 在获得有效 PTE 后调用 vm_normal_page() 转换物理页。

若 vm_normal_page() 返回非 NULL,follow_page_mask() 将其结果(struct page*)返回给 get_user_pages(),后者再固定页面。

典型调用场景

// 示例:在 follow_page_mask() 中调用

pte_t pte = *pte_offset_map(...);

if (pte_present(pte)) {

    struct page *page = vm_normal_page(vma, addr, pte);

    if (page) 

        return page; // 返回物理页描述符

}

return NULL; // PTE 无效或特殊页

总结:vm_normal_page() 是内存管理基础设施中的精确定向转换器,专注解决 “有效的 PTE 对应哪个物理页?” 的问题,为上层提供标准物理页描述符,同时屏蔽零页/设备内存等特殊细节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux技术芯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值