sparse memory内存模型中page_to_pfn()的实现原理

内存模型

Linux 内核把页面当作物理内存管理的基本单位(一般情况下,虚拟页叫页面,而物理页叫页帧)。每个页帧对应一个 struct page 结构体(页描述符),这是一个体系结构无关的数据结构。pfn 是物理页的编号。
在内核中,每个页帧必然有唯一一个页描述符与之对应。于是,页描述符的地址与pfn之间存在对应关系,即 page_to_pfn() 和 pfn_to_page()。
同时,Linux 内核支持 3 种不同的“内存模型”,分别是:Flat Memory、Discontigous Memory、Sparse Memory。
对于 Sparse Memory 模型,还有两个变体: Sparse Extrem Memory、Sparse Vmemmap Memory。

Sparse Memory内存模型

在 Sparse Memory 模型中,内存按照 section 进行划分,每个 section 用 struct mem_section 来描述(对于 x86 而言,一个 section 是 128MB)。其中在 struct mem_section 的 section_mem_map 变量中存了该 section 对应的 struct page 数组的首地址(实际上里面还编码了当前 section 起始 pfn 的偏移量,这确保了下面第四步计算结果的正确性)。
在 Sparse Memory 模型中,由 page_to_pfn() 需要四步:

  • 由 page 结构体得到其所在的 section 编号
  • 由 section 编号得到 section 结构体
  • 由 section 结构体得到其对应的 struct page 数组首地址
  • 由 page 首地址减去 struct page 数组首地址得到物理帧号

pfn_to_page() 反之亦然。在 Sparse Memory 模型中,pfn 是连续的,但 struct page 数组不见得是连续的,也可以是分段的。

Sparse Vmemmap Memory内存模型

在 Sparse Vmemmap Memory 模型中,struct page 数组是通过页表映射进行访问的。
其中 struct page 数组的首地址是 vmemmap,并且 struct page 数组在虚拟地址空间是连续的,因此 page_to_pfn() 只需要一步, 就等于 page - vmemmap。pfn_to_page() 反之亦然,和 Sparse Memory 模型相比,page_to_pfn() 的计算更加简单,不需要访存即可得到结果。

内核代码

#define page_to_pfn __page_to_pfn
#define pfn_to_page __pfn_to_page

#if defined(CONFIG_SPARSEMEM_VMEMMAP)                       
/* memmap is virtually contiguous.  */
#define __pfn_to_page(pfn)  (vmemmap + (pfn))
#define __page_to_pfn(page) (unsigned long)((page) - vmemmap)
                        
#elif defined(CONFIG_SPARSEMEM)
/*                      
 * Note: section's mem_map is encoded to reflect its start_pfn.
 * section[i].section_mem_map == mem_map's address - start_pfn;
 */                     
#define __page_to_pfn(pg)                   \
({  const struct page *__pg = (pg);             \
    int __sec = page_to_section(__pg);          \
    (unsigned long)(__pg - __section_mem_map_addr(__nr_to_section(__sec))); \
})                      
                        
#define __pfn_to_page(pfn)              \
({  unsigned long __pfn = (pfn);            \
    struct mem_section *__sec = __pfn_to_section(__pfn);    \
    __section_mem_map_addr(__sec) + __pfn;      \
})                      
#endif /* CONFIG_FLATMEM/DISCONTIGMEM/SPARSEMEM */

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值