在linux中,pfn全称“page frame number”,是物理内存区域编号。“page frame”是针对物理内存而言的,把物理内存分成一个个的page size的区域,并且给每一个page编号,而这个编号就是PFN。
本教程操作环境:linux5.9.8系统、Dell G3电脑。
1、什么是page frame?
操作系统最重要的作用之一就是管理计算机系统中的各种资源,做为最重要的资源:内存,我们必须管理起来。在linux操作系统中,物理内存是按照page size来管理的,具体page size是多少是和硬件以及linux系统配置相关的,4k是最经典的设定。因此,对于物理内存,我们将其分成一个个按page size排列的page,每一个物理内存中的page size的内存区域我们称之page frame。我们针对每一个物理的page frame建立一个struct page的数据结构来跟踪每一个物理页面的使用情况:是用于内核的正文段?还是用于进程的页表?是用于各种file cache还是处于free状态……
每一个page frame有一个一一对应的page数据结构,系统中定义了page_to_pfn和pfn_to_page的宏用来在page frame number和page数据结构之间进行转换,具体如何转换是和memory modle相关,我们会在第三章详细描述linux kernel中的3种内存模型。
2、什么是PFN?
对于一个计算机系统,其整个物理地址空间应该是从0开始,到实际系统能支持的最大物理空间为止的一段地址空间。在ARM系统中,假设物理地址是32个bit,那么其物理地址空间就是4G,在ARM64系统中,如果支持的物理地址bit数目是48个,那么其物理地址空间就是256T。当然,实际上这么大的物理地址空间并不是都用于内存,有些也属于I/O空间(当然,有些cpu arch有自己独立的io address space)。因此,内存所占据的物理地址空间应该是一个有限的区间,不可能覆盖整个物理地址空间。不过,现在由于内存越来越大,对于32位系统,4G的物理地址空间已经无法满足内存的需求,因此会有high memory这个概念,后续会详细描述。
PFN是page frame number的缩写,所谓page frame,就是针对物理内存而言的,把物理内存分成一个个的page size的区域,并且给每一个page 编号,这个编号就是PFN。假设物理内存从0地址开始,那么PFN等于0的那个页帧就是0地址(物理地址)开始的那个page。假设物理内存从x地址开始,那么第一个页帧号码就是(x>>PAGE_SHIFT)。
PFN的取值范围是 0—-(memory size >> 12)。
但是由于物理内存映射的关系,物理内存的0地址对应到到系统上并不是物理地址的0。 例如:s3c2440上,内存的地址是从0x30000000开始的。当连接ram后,ram的0地址在s3c2440看来,就是0x30000000. 所以在系统中 pfn的值 应该等于 (physical address – memory base address) >> 12 。
但是linux中,用的又都是虚拟地址,所以要先将 virtual address转换成 physical address 才行。 所以在linux中有个宏定义 将内核线性空间的虚拟地址和pfn转换:
#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) //__pa() 将虚拟地址转化成物理地址 #define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT)
所以pfn在linux内核中应该对应的是页帧号。可以通过简单的转换找到对应的物理地址。
每个物理上的页,内核给与之分配了一个描述符来描述: page。 pfn可以和page进行转换:
#define page_to_pfn __page_to_pfn #define pfn_to_page __pfn_to_page
所有的page结构存放在mem_map中,方便进行管理。
由于linux是将物理内存分成4K大小的页来进行管理的。所以在软件上也会设置MMU进行匹配对应。后面叙述。
linux将4G的虚拟空间分成了用户空间和内核空间。用户空间是0—3G,内核空间是3G—4G。 从内核空间可以访问到用户空间,但是从用户空间必须通过系统调用来能访问内核空间。