内核中page table决定了一个虚拟地址如何查找到对应的物理地址关键,维护着整个整个虚拟地址与物理地址对应信息,是实现整个内存虚拟化的基础。在虚拟地址到物理地址的转换过程中为了减少整个物理空间的占用以及解决内存中可能存在的空洞,page table 将一个线性地址(即虚拟地址)按照多级划分的划分,减少转换关系数组占用的物理空间。同时在多级划分过程中,内核为了兼容不同的芯片架构,将整个分级划分进行了抽象处理可以根据不同的架构进行配置,32位系统下支持三或者二级查找,64位系统下支持四级或者五级查找:

A page table, of course, maps a virtual memory address to the physical address where the data is actually stored. It is conceptually a linear array indexed by the virtual address (or, at least, by the page-frame-number portion of that address) and yielding the page-frame number of the associated physical page. Such an array would be large, though, and it would be hugely wasteful. Most processes don't use the full available virtual address space even on 32-bit systems, and they don't use even a tiny fraction of it on 64-bit systems, so the address space tends to be sparsely populated and, as a result, much of that array would go unused.

32位系统

32位系统将一个线性地址(虚拟地址)转换成物理地址转换关系如下:

linux内核针对32位系统 地址转换采用一个三级映射的划分,主要是为了兼容各个硬件架构(注意内核的三级映射划分其实严格来说和 硬件地址转换并不是一个概念,只是为了更好兼容硬件一般理解为硬件MMU转换过程),其中在task_struct中mm->pgd,用户保存每个进程的自己的地址转换关系。一个32位的虚拟地址被整个分隔成四块,分别为GLobal PGD、Middle PMD、Table PTE、Page Offset这样一个划分,采用上述分级划分主要是为了考虑解决维护地址关键表占用内存较多问题。

mm->pgd保存着每个进程地址转换关系pgd_t表,通过从32位地址中PGD部分获取到index,作为相应的pdg_t表所有,获取到下一级用于地址转换关系的pmd_t表,使用32位地址中的PMD 作为pmd_t表索引,寻找到pte_t表,继续用PTE 作为索引获取到PTE表的中entry,此是entry中保存的物理内存page 的 PFN(page frame number),用做映射到物理内存页,同时使用虚拟地址的page offset作为 页内偏移,寻找到对应的具体物理地址,这样在转换过程中需要至少三次访问内存,效率比较底下。为了提高转换效率,CPU内部都集成MMU硬件,用于通过硬件进程转换,其中mm->pgd在X86架构下一般都是在进程切换过程中将地址保存到CR3寄存器中,MMU在转换过程中M可以将部分转换关系直接保存到TLB硬件缓存中。

有些硬件架构采用二级映射方式,内核可以通过配置将PMD所对应的size配置为0即可。

地址转换关系总结为PGD->PMD->PTE,内核为了方便使用每一级都对应的相应的宏,来表示在所对应的偏移位,分别位PGDIR_SHIFT、PMD_SHIFT、PTE_SHIFT以及PAGE_SHIFT

X86 32位 3级page table

以下x86 32位架构定义如下:

x86 32 采用三级 page table,定义文件位于(arch/x86/include/asm/pgtable-3level_types.h):

/** PGDIR_SHIFT determines what a top-level page table entry can map*/
#define PGDIR_SHIFT 30
#define PTRS_PER_PGD    4/** PMD_SHIFT determines the size of the area a middle-level* page table can map*/
#define PMD_SHIFT   21
#define PTRS_PER_PMD    512/** entries per page directory level*/
#define PTRS_PER_PTE    512
PTE_SHIFT定义位于(/arch/x86/include/asm/pgtable.h):
#define PTE_SHIFT ilog2(PTRS_PER_PTE)

PAGE_SHITF定义位于(/arch/x86/include/asm/page_types.h)文件中:

/* PAGE_SHIFT determines the page size */
#define PAGE_SHIFT      12
  • PTRS_PER_PTE 大小定义为512即 pte_t表大小为512个共占用9位,page 占用12位即一般PTE_SHITF和PAGE_SHIFT相等即都是偏移12位。
  • pmd_t表大小同样是512个占用9位,偏移从21开始
  • pgd_t表大小共4个占用2两,从30位开始

X86 32位 2级page table

针对32位系统还有另外一种查表方式 即按照二级转换方式,二级转换方式取消了pmd_t表,各个字段定义如下定义如下(位于/arch/x86/include/asm/pgtable-2level_types.h):

/** traditional i386 two-level paging structure:*/#define PGDIR_SHIFT    22
#define PTRS_PER_PGD    1024/** the i386 is two-level, so we don't really have any* PMD directory physically.*/#define PTRS_PER_PTE    1024
  •  PTRS_PER_PTE 表大小位1024占用10位,从12~21位.。
  • pgt_t表大小同样为1024,占用22~31位。

64位系统

由于64位系统要比32位系统地址要长,为了转换方便,增加了地址转换级数,可以分别配置为四级或者五级地址转换,进行分别介绍:

4级page table

4级 page table划分如下:

  • PGD和PMD之间又增加了一个PUD表,用于增加转换级数
  • 从《地址空间划分》详细可以查看到虚拟地址空间分布使用状况,其中4级page table中虚拟地址最高使用到47位地址空间,63~48位处于unused状态即不用于地址空间转换。
  • 64位虚拟地址空间转换成物理地址需要查找表项位 PGD->PUD->PMD->PTES->PAGE
  • PGD 占用 39~47位
  • PUD 占用 30~38位
  • PMD 占用 21~29位
  • PTE 占用 12~20位
  • PAGE 大小位4K 0~11位
  • 为了进行地址转换至少需要五次内存访问
  • 增加PUD_SHIFT用于说明PUD表位偏移。
  • 由于虚拟地址最高到47位,因此虚拟地址空间范围能够覆盖128TB

 To translate that address, the hardware splits the address into several bit fields. Note that, in the scheme shown here (corresponding to how the x86-64 architecture uses addresses), the uppermost 16 bits are discarded; only the lower 48 bits of the virtual address are used. Of the bits that are used, the nine most significant (bits 39-47) are used to index into the page global directory (PGD); a single page for each address space. The value read there is the address of the page upper directory (PUD); bits 30-38 of the virtual address are used to index into the indicated PUD page to get the address of the page middle directory (PMD). With bits 21-29, the PMD can be indexed to get the lowest level page table, just called the PTE. Finally, bits 12-20 of the virtual address will, when used to index into the PTE, yield the physical address of the actual page containing the data. The lowest twelve bits of the virtual address are the offset into the page itself.

4级页表主要偏移定义位于(/arch/x86/include/asm/pgtable_64_types.h)文件内,主要定义如下:

/** PGDIR_SHIFT determines what a top-level page table entry can map*/
#define PGDIR_SHIFT     39
#define PTRS_PER_PGD        512
#define MAX_PTRS_PER_P4D    1/** 3rd level page*/
#define PUD_SHIFT   30
#define PTRS_PER_PUD    512** PMD_SHIFT determines the size of the area a middle-level* page table can map*/
#define PMD_SHIFT   21
#define PTRS_PER_PMD    512/** entries per page directory level*/
#define PTRS_PER_PTE    512#define PMD_SIZE (_AC(1, UL) << PMD_SHIFT)
#define PMD_MASK    (~(PMD_SIZE - 1))
#define PUD_SIZE    (_AC(1, UL) << PUD_SHIFT)
#define PUD_MASK    (~(PUD_SIZE - 1))
#define PGDIR_SIZE  (_AC(1, UL) << PGDIR_SHIFT)
#define PGDIR_MASK  (~(PGDIR_SIZE - 1))

一个四级 物理地址转换关系(其他级别的类似)可以用如下说明:

其中虚拟地址的0~11位用作物理地址的页内偏移。 

5级page table

5级page table 最开始从4.12版本中开始引入,其支持的地址范围达到了57位,由于虚拟地址范围增加,需要增加相应地址转换关系级数,5级page table转换关系如下:

  •  五级地址转换中新增P4D表,位于PGD 和PUD之间
  • 虚拟 地址转换成物理地址依次经过 PGD-->P4D-->PUD-->PMD-->PTE-->PAGE
  • 地址寻址空间最高增加到57位,达到128PiB
  • PGD 可以根据需要配置,默认占用48~57
  • P4D,占用39~47
  • PUD 占用 30~38位
  • PMD 占用 21~29位
  • PTE 占用 12~20位
  • PAGE 大小位4K 0~11位
  • 增加P4D_SHIT用于 p4d表索引偏移

五级page table 各个偏移定义位于(/arch/x86/include/asm/pgtable_64_types.h)文件中:

/** PGDIR_SHIFT determines what a top-level page table entry can map*/
#define PGDIR_SHIFT pgdir_shift
#define PTRS_PER_PGD    512/** 4th level page in 5-level paging case*/
#define P4D_SHIFT       39
#define MAX_PTRS_PER_P4D    512
#define PTRS_PER_P4D        ptrs_per_p4d
#define P4D_SIZE        (_AC(1, UL) << P4D_SHIFT)
#define P4D_MASK        (~(P4D_SIZE - 1))#define MAX_POSSIBLE_PHYSMEM_BITS  52... .../** 3rd level page*/
#define PUD_SHIFT   30
#define PTRS_PER_PUD    512/** PMD_SHIFT determines the size of the area a middle-level* page table can map*/
#define PMD_SHIFT   21
#define PTRS_PER_PMD    512/** entries per page directory level*/
#define PTRS_PER_PTE    512#define PMD_SIZE (_AC(1, UL) << PMD_SHIFT)
#define PMD_MASK    (~(PMD_SIZE - 1))
#define PUD_SIZE    (_AC(1, UL) << PUD_SHIFT)
#define PUD_MASK    (~(PUD_SIZE - 1))
#define PGDIR_SIZE  (_AC(1, UL) << PGDIR_SHIFT)
#define PGDIR_MASK  (~(PGDIR_SIZE - 1))

Page Table 兼容问题

兼容不同级别页表

内核page table分级划分毕竟只是软件抽象的一种资源划分,最后根本上还是要兼容各种硬件架构,比如在32位系统架构下,有些硬件支持三级转换,有些硬件支持二级别映射,如果是一个二级映射,则软件配置没有PMD表,如果是64位系统 四级映射,则不存在P4D表,P4D表转换过程中直接返回,不处理,如下:

static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
{return (p4d_t *)pgd;
}

32位 二级映射表中直接将PGDIR_SHIFT 设置位22位偏移,直接将PMD表取消:

同样64位 四级页表,直接将P4D覆盖取消:

兼容huge page

内核管理物理内存一般默认都是按照page 大小为4K进行管理,但是在有些系统或者场景下需要使用需要使用4M 大小的page 进行管理 以便减少内存管理占用的数据和减少TLB miss的机率。当page 大小为 4M时,内核通过取消PTE表,以合并成一个更大的页 即4M作为管理单元,如下图:

huge page 2M 转换关系可以用如下图说明:

通过将PTE和 Page offset合并组成一个更大page 单元进行管理:

At any level of the page table, the pointer to the next level can be null, indicating that there are no valid virtual addresses in that range. This scheme thus allows large subtrees to be missing, corresponding to ranges of the address space that have no mapping. The middle levels can also have special entries indicating that they point directly to a (large) physical page rather than to a lower-level page table; that is how huge pages are implemented. A 2MB huge page would be found directly at the PMD level, with no intervening PTE page, for example

CONFIG_PGTABLE_LEVELS

CONFIG_PGTABLE_LEVELS可以用于配置内核 page table级数,一般默认x86 64系统级数为四级,32位 x86系统默认位三级转换,具体使用哪中转换级数 与CPU硬件架构密不可分的,可以使用如下命令查了当前系统使用的page table 转换级数:

# cat /boot/config-$(uname -r) | grep -E 'X86_5LEVEL|PGTABLE_LEVELS'

例如:

# cat /boot/config-$(uname -r) | grep -E 'X86_5LEVEL|PGTABLE_LEVELS'
CONFIG_PGTABLE_LEVELS=5
CONFIG_X86_5LEVEL=y

MMU/TLB(Translation looaside buffers)

cpu每次将虚拟地址转换成物理地址都需要进行好几次内存读取,无疑成为系统运行的一个主要瓶颈,为了加快整个转换过程,CPU将经常访问到的虚拟地址对应的物理地址转换关系存储到TLB硬件单元中,以加快整个地址转换过程,TLB可以认为是一个特殊的cache硬件单元,里面存储着部分地址的转换关系,而TLB硬件单元一般都是设计到MMU内存管理单元中,方便整个内存地址转换关键硬件管理。尽量增加TLB 命中是程序性能优化的一个关键点,一般而言应用程序并不能控制MMU,可以尽量访问最近访问的地址以加大TLB命中机率。

CPU通过TLB可以直接获取到虚拟地址对应的物理PFN,再结合虚拟地址中的页偏移组成一个物理地址, 下图是对TLB硬件一个简单的抽象:

TLB一个虚拟地址转换成对应的物理地址称为一条entry,每个entry存放对应的物理PFN,权限等,下图为x86 64位系统下 每个entry存放的条目,12~52位存放的是对应物理PFN, bit  8表明该转换是否是global,bit 6表明是否是一个脏页, bit 1 和 bit 2为对应的读写权限等,不再详细描述。

 TLB本质上就是一个特殊的cache,由于成本、工艺限制,TLB不可能做的太大,为了提高TLB 命中率及转换效率,现代CPU内部都设计了多级TLB及L1 TLB和L2 TLB以增加转换效率:

 L1 TLB一般与CPU相距很进,访问速率很快,但是毕竟小 一般大小可以存放 128 条entry, 而L2 TLB可以存放 1536 条entry,  距离CPU 比L1 TLB要远,一个单核二级TLB 平均访问内存延迟计算如下:

多级TLB的设计与具体硬件架构有关,直接影响着系统访问内存的效率。

参考资料

Five-level page tables [LWN.net]

https://cateee.net/lkddb/web-lkddb/PGTABLE_LEVELS.html

《Architectural and Operating System Support for Virtual Memory》

linux那些事之page table相关推荐

  1. linux那些事之 page table基本操作

    通过遍历page table查找一个虚拟地址已经做了对应的物理内存映射是一个很常用的操作,因此整个walk遍历过程耗时要尽量小. show_pte() 以show_pte()函数为例说明如何根据一个虚 ...

  2. linux那些事之page fault(AMD64架构)(user space)(2)

    do_user_addr_fault 用户空间地址处理是page fault主要处理流程,x86 64位系统主要是do_user_addr_fault()函数 该处理部分是x86架构特有部分 即与架构 ...

  3. linux那些事之 page translation(硬件篇)

    Page Translation 以<AMD64 Architecture Programmer's manual volums>从硬件角度说明一个虚拟地址如何转成对应物理页.AM64 地 ...

  4. linux那些事之page cache

    page cache page cache又称高速缓存,主要是针对文件文件系统,为了减少不必要的磁盘IO操作(读/写)造成卡顿问题,内核将磁盘文件中的内容缓存到内存中,并选择适当时机对磁盘进行读写操作 ...

  5. linux那些事之page fault(AMD64架构)(1)

    应用程序或者内核都是运行在虚拟内存空间之中,kernel 启动完成之后如果一个虚拟地址要访问物理内存需要通过CPU MMU硬件进行地址转换,整个虚拟地址访问物理内存逻辑过程如下: kernel 启动完 ...

  6. Linux内存page,Linux虚拟内存管理 - Page Table的作用

    虚拟内存的作用: 1. 扩展实际有限的物理内存,当然这种扩展是虚拟的,比如物理内存512M,对于一个需要1G空间的进程来说,照样可以运行.这增加了操作系统是应用范围. 2. 使得进程中的数据空间增大, ...

  7. linux那些事之TLB(Translation-Lookaside Buffer)无效操作

    TLB 为了加速虚拟地址转换物理地址过程,CPU内部一般都集成TLB硬件单元,通过缓存存取虚拟地址与物理地址映射关系,避免再通过MMU 通过多级查表引入多次内存开销,直接将映射关系存储到硬件单元中,本 ...

  8. linux那些事之zero page

    zero page zero page是一个特殊的物理页,里面值全部为0,zero page是针对匿名页场景专门进行优化,主要是节省内存和对性能进行了一定优化.当malloc或者mapp一段虚拟内存后 ...

  9. linux那些事之LRU(3)

    继续承接<linux那些事之LRU(2)>,shrink_active_list()函数中需要将调用isolate_lru_pages函数将active LRU中从链表尾部隔离出nr_to ...

最新文章

  1. Metasploit makerc命令技巧
  2. 高通平台:USB充电
  3. 曲线运动与万有引力公式_高考物理曲线运动万有引力专题讲解
  4. Hadoop之MapTask工作机制
  5. ABB 压包指令PackRawBytes 解包指令UnpackRawBytes
  6. 无忧开通了博客园博客主页
  7. java 字典 引用_java中数据字典的使用
  8. php7 fastcgi安装,安装windows 下 php7+nginx+fastcgi
  9. Spring Boot源码分析
  10. K.image_data_format() == ‘channels_first‘
  11. JAVA编译器eclipse的安装教程
  12. SAP: Query创建教程
  13. 打包labview程序
  14. 【高等数学】高阶偏导数与隐函数的高阶偏导数的深度辨析
  15. java mp3文件合并,java怎么实现mp3合并
  16. linux查看运行的虚拟机,windows,linux,esxi系统判断当前主机是物理机还是虚拟机?查询主机序列号命令...
  17. There was a problem confirming the ssl certificate: HTTPSConnectionPool(host=‘pypi.org‘,port=443)
  18. JAVA第八课:集合(CollectionMap)
  19. Dremel学习总结1
  20. javaEE项目--琪琪线上餐厅系统

热门文章

  1. 浅谈分布式消息技术 Kafka
  2. Git上传Github及基本操作
  3. 数据结构基础(2) --顺序查找 二分查找
  4. 算法的力量万变不离其宗 -- 李开复
  5. OpenStack第十四个版本及14项重要事实
  6. 手把手教你用.NET Core写爬虫
  7. 元素上下层叠关系总结
  8. 路由器端口映射实现远程桌面
  9. win7下node.js设置npm环境变量
  10. 寻找是生命中的另一场迷失