Linux内存管理6(基于6.1内核)---分页机制
一、分页机制
在虚拟内存中,页表是个映射表的概念, 即从进程能理解的线性地址(linear address)映射到存储器上的物理地址(phisical address)。
1.1 为什么使用多级页表来完成映射
但是为什么要使用多级页表来完成映射呢?
多级页表(Multi-Level Page Table)来完成虚拟地址到物理地址的映射,主要是为了优化内存管理,解决传统单级页表在大规模内存系统中存在的一些效率和资源消耗问题。多级页表可以提供更好的空间效率、减少内存占用并提高系统的扩展性。下面是使用多级页表的几个主要原因:
1. 减少页表占用的内存
在传统的单级页表中,每一个虚拟页(通常为 4KB 或 8KB)都会有一个独立的页表条目。在现代操作系统中,如果直接为每个虚拟地址分配一个页表条目,且虚拟地址空间很大(比如 32 位地址空间 4GB 或 64 位地址空间更大),单级页表的大小会非常庞大,导致巨大的内存开销。
- 多级页表的优势:通过将页表分层,每一级页表都只存储一部分信息,不需要为每一个虚拟地址分配一个完整的页表条目。这样做的好处是,只有在需要的时候(即访问的虚拟地址被映射到物理地址时),才会分配对应的页表项,避免了大范围的内存浪费。
例如,在 32 位系统中,虚拟地址空间可以有 4GB,但对于一个单级页表来说,假设每个页表项需要 4 字节,那么需要的页表大小就是几百 MB,且在大多数情况下,实际的物理内存远小于虚拟内存空间。如果用多级页表来管理,只有在实际使用的地址段才会有对应的页表项。
2. 扩展性和灵活性
-
虚拟地址空间的增加:随着计算机硬件和操作系统的发展,地址空间的需求越来越大。单级页表在大虚拟地址空间下(如 64 位系统的 16EB 地址空间)无法直接处理。多级页表通过引入多个级别的页表,使得地址空间的扩展变得更加灵活和可管理。每个级别的页表都处理一部分虚拟地址,避免了一开始就为所有可能的虚拟地址分配页表。
-
内存的分配更加高效:多级页表允许根据实际使用的虚拟地址空间来动态分配页表条目。只有需要映射的虚拟页面才会分配页表项,而不需要为每个虚拟地址都预留页表空间。这种分配方式节省了内存,并提高了系统的扩展性。
3. 减少页表查找的开销
-
分级查找:多级页表通过将虚拟地址分割成多个部分,每部分对应不同级别的页表,可以显著减少查找页表的复杂度。假设虚拟地址由多个段(如页目录、页表项)构成,每个级别的页表只需要查找该级别的对应表项,最终获得物理地址。
-
页表分段:通过分级查找,操作系统可以有效地减少内存访问次数。例如,对于 32 位系统,通常可以将虚拟地址分为多个字段(如页目录、页表、页内偏移),每次查找只需要访问其中一部分,从而降低了查找的时间开销。
1.2 32位系统中2级页表
从80386开始, intel处理器的分页单元是4KB的页, 32位的地址空间被分为3部分:
| 单元 | 描述 |
|---|---|
| 页目录表Directory | 最高10位 |
| 页中间表Table | 中间10位 |
| 页内偏移 | 最低12位 |
即页表被划分为页目录表Directory和页中间表Tabl两个部分:
此种情况下, 线性地址的转换分为两步完成:
-
第一步, 基于两级转换表(页目录表和页中间表), 最终查找到地址所在的页帧
-
第二步, 基于偏移, 在所在的页帧中查找到对应偏移的物理地址
使用这种二级页表可以有效的减少每个进程页表所需的RAM的数量. 如果使用简单的一级页表, 那将需要高达220220个页表, 假设每项4B, 则共需要占用220∗4B=4MB220∗4B=4MB的RAM来表示每个进程的页表. 当然我们并不需要映射所有的线性地址空间(32位机器上线性地址空间为4GB), 内核通常只为进程实际使用的那些虚拟内存区请求页表来减少内存使用量。
1.3 64位系统中的分页
正常来说, 对于32位的系统两级页表已经足够了, 但是对于64位系统的计算机, 这远远不够。
首先假设一个大小为4KB的标准页. 因为1KB覆盖210210个地址的范围, 4KB覆盖212212个地址, 所以offset字段需要12位。
这样线性地址空间就剩下64-12=52位分配给页中间表Table和页目录表Directory. 如果我们现在决定仅仅使用64位中的48位来寻址(这个限制其实已经足够了, 2^48=256TB, 即可达到256TB的寻址空间). 剩下的48-12=36位被分配给Table和Directory字段. 即使我们现在决定位两个字段各预留18位, 那么每个进程的页目录和页表都包含218218个项, 即超过256000个项。
基于这个原因, 所有64位处理器的硬件分页系统都使用了额外的分页级别. 使用的级别取决于处理器的类型:
| 平台名称 | 页大小 | 寻址所使用的位数 | 分页级别数 | 线性地址分级 |
|---|---|---|---|---|
| alpha | 8KB | 43 | 3 | 10 + 10 + 10 + 13 |
| ia64 | 4KB | 39 | 3 | 9 + 9 + 9 + 12 |
| ppc64 | 4KB | 41 | 3 | 10 + 10 + 9 + 12 |
| sh64 | 4KB | 41 | 3 | 10 + 10 + 9 + 12 |
| x86_64 | 4KB | 48 | 4 | 9 + 9 + 9 + 9 + 12 |
1.4 Linux中的分页
层次话的页表用于支持对大地址空间快速, 高效的管理。因此linux内核堆页表进行了分级。
前面我们提到过, 对于32位系统中, 两级页表已经足够了。 但是64位修奥更多数量的分页级别。
为了同时支持适用于32位和64位的系统, Linux采用了通用的分页模型。 在Linux-2.6.10版本中, Linux采用了三级分页模型。 而从2.6.11开始普遍采用了四级分页模型。
目前的内核的内存管理总是嘉定使用四级页表,而不管底层处理器是否如此。
| 单元 | 描述 |
|---|---|
| 页全局目录 | Page GlobalDirectory |
| 页上级目录 | Page Upper Directory |
| 页中间目录 | Page Middle Directory |
| 页表 | Page Table |
| 页内偏移 | Page Offset |
Linux不同于其他的操作系统, 它把计算机分成独立层(体系结构无关)/依赖层(体系结构相关)两个层次。对于页面的映射和管理也是如此。 页表管理分为两个部分, 第一个部分依赖于体系结构, 第二个部分是体系结构无关的. 所有数据结构几乎都定义在特定体系结构的文件中. 这些数据结构的定义可以在头文件arch/对应体系/include/asm/page.h和arch/对应体系/include/asm/pgtable.h中找到。但是对于AMD64和IA-32已经统一为一个体系结构。 但是在处理页表方面仍然有很多的区别, 因为相关的定义分为两个不同的文件arch/x86/include/asm/page_32.h和arch/x86/include/asm/page_64.h, 类似的也有pgtable_xx.h。
二、页表
Linux内核通过四级页表将虚拟内存空间分为5个部分(4个页表项用于选择页, 1个索引用来表示页内的偏移)。各个体系结构不仅地址长度不同, 而且地址字拆分的方式也不一定相同. 因此内核使用了宏用于将地址分解为各个分量。
在Linux内核中,使用四级页表来管理虚拟内存,通常适用于64位架构(如x86_64)。四级页表将虚拟内存分为多个部分,每一部分对应一个页表项。以下是四级页表的结构概述:
-
虚拟地址结构: 虚拟地址分为五个部分:
- P4 (页表层级 4): 用于选择第一级页表的项
- P3 (页表层级 3): 用于选择第二级页表的项
- P2 (页表层级 2): 用于选择第三级页表的项
- P1 (页表层级 1): 用于选择第四级页表的项
- 偏移量: 表示在页面内的具体位置
-
图示(假设虚拟地址为 48 位虚拟地址):
-
+--------------------+------------------+------------------+------------------+-----------------+ | 物理页号 (P4) | 物理页号 (P3) | 物理页号 (P2) | 物理页号 (P1) | 偏移量 | +--------------------+------------------+------------------+------------------+-----------------+ | 9 位 | 9 位 | 9 位 | 9 位 | 12 位 | +--------------------+------------------+------------------+------------------+-----------------+- P4, P3, P2, P1 各自占用9位(共36位),选择不同的页表层级。
- 偏移量 占12位,表示页内具体偏移。
-
页表结构: 每一层的页表项存储指向下一层页表或最终物理页框的地址。最底层的页表项指向实际的物理页面地址。
三、Linux分页机制的演变
3.1 Linux的页表实现
由于程序存在局部化特征, 这意味着在特定的时间内只有部分内存会被频繁访问,具体点,进程空间中的text段(即程序代码), 堆, 共享库,栈都是固定在进程空间的某个特定部分,这样导致进程空间其实是非常稀疏的, 于是,从硬件层面开始,页表的实现就是采用分级页表的方式,Linux内核当然也这么做。所谓分级简单说就是,把整个进程空间分成区块,区块下面可以再细分,这样在内存中只要常驻某个区块的页表即可,这样可以大量节省内存。
3.2 Linux最初的二级页表
Linux最初是在一台i386机器上开发的,这种机器是典型的32位X86架构,支持两级页表
一个32位虚拟地址如上图划分。当在进行地址转换时,
结合在CR3寄存器中存放的页目录(page directory, PGD)的这一页的物理地址,再加上从虚拟地址中抽出高10位叫做页目录表项(内核也称这为pgd)的部分作为偏移, 即定位到可以描述该地址的pgd;
从该pgd中可以获取可以描述该地址的页表的物理地址,再加上从虚拟地址中抽取中间10位作为偏移, 即定位到可以描述该地址的pte;
在这个pte中即可获取该地址对应的页的物理地址, 加上从虚拟地址中抽取的最后12位,即形成该页的页内偏移, 即可最终完成从虚拟地址到物理地址的转换。
从上述过程中,可以看出,对虚拟地址的分级解析过程,实际上就是不断深入页表层次,逐渐定位到最终地址的过程,所以这一过程被叫做page talbe walk。
至于这种做法为什么能节省内存,举个更简单的例子更容易明白。比如要记录16个球场的使用情况,每张纸能记录4个场地的情况。采用4+4+4+4,共4张纸即可记录,但问题是球场使用得很少,有时候一整张纸记录的4个球场都没人使用。于是,采用4 x 4方案,即把16个球场分为4组,同样每张纸刚好能记录4组情况。这样,使用一张纸A来记录4个分组球场情况,当某个球场在使用时,只要额外使用多一张纸B来记录该球场,同时,在A上记录”某球场由纸B在记录”即可。这样在大部分球场使用很少的情况下,只要很少的纸即困记录,当有球场被使用,有需要再用额外的纸来记录,当不用就擦除。这里一个很重要的前提就是:局部性。
3.3 Linux的三级页表
当X86引入物理地址扩展(Pisycal Addrress Extension, PAE)后,可以支持大于4G的物理内存(36位),但虚拟地址依然是32位,原先的页表项不适用,它实际多4 bytes被扩充到8 bytes,这意味着,每一页现在能存放的pte数目从1024变成512了(4k/8)。相应地,页表层级发生了变化,Linus新增加了一个层级,叫做页中间目录(page middle directory, PMD), 变成:
| 字段 | 描述 | 位数 |
|---|---|---|
| cr3 | 指向一个PDPT | crs寄存器存储 |
| PGD | 指向PDPT中4个项中的一个 | 位31~30 |
| PMD | 指向页目录中512项中的一个 | 位29~21 |
| PTE | 指向页表中512项中的一个 | 位20~12 |
| page offset | 4KB页中的偏移 | 位11~0 |
实际的page table walk依然类似,只不过多了一级。
现在就同时存在2级页表和3级页表,在代码管理上肯定不方便。巧妙的是,Linux采取了一种抽象方法:所有架构全部使用3级页表: 即PGD -> PMD -> PTE。那只使用2级页表(如非PAE的X86)怎么办?
办法:针对使用2级页表的架构,把PMD抽象掉,即虚设一个PMD表项。这样在page table walk过程中,PGD本直接指向PTE的,现在不了,指向一个虚拟的PMD,然后再由PMD指向PTE。这种抽象保持了代码结构的统一。
3.4 Linux的四级页表
硬件在发展,3级页表很快又捉襟见肘了,原因是64位CPU出现了, 比如X86_64, 它的硬件是实实在在支持4级页表的。它支持48位的虚拟地址空间。如下:
| 字段 | 描述 | 位数 |
|---|---|---|
| PML4 | 指向一个PDPT | 位47~39 |
| PGD | 指向PDPT中4个项中的一个 | 位38~30 |
| PMD | 指向页目录中512项中的一个 | 位29~21 |
| PTE | 指向页表中512项中的一个 | 位20~12 |
| page offset | 4KB页中的偏移 | 位11~0 |
Linux内核针为使用原来的3级列表(PGD->PMD->PTE),做了折衷。即采用一个唯一的,共享的顶级层次,叫PML4[2]。这个PML4没有编码在地址中,这样就能套用原来的3级列表方案了。不过代价就是,由于只有唯一的PML4, 寻址空间被局限在(239=)512G, 而本来PML4段有9位, 可以支持512个PML4表项的。现在为了使用3级列表方案,只能限制使用一个, 512G的空间很快就又不够用了,解决方案呼之欲出。
在2004年10月,当时的X86_64架构代码的维护者Andi Kleen提交了一个叫做4level page tables for Linux的PATCH系列,为Linux内核带来了4级页表的支持。在他的解决方案中,不出意料地,按照X86_64规范,新增了一个PML4的层级, 在这种解决方案中,X86_64拥一个有512条目的PML4, 512条目的PGD, 512条目的PMD, 512条目的PTE。对于仍使用3级目录的架构来说,它们依然拥有一个虚拟的PML4,相关的代码会在编译时被优化掉。 这样,就把Linux内核的3级列表扩充为4级列表。这系列PATCH工作得不错,不久被纳入Andrew Morton的-mm树接受测试。
不出意外的话,它将在v2.6.11版本中释出。但是,另一个知名开发者Nick Piggin提出了一些看法,他认为Andi的Patch很不错,不过他认为最好还是把PGD作为第一级目录,把新增加的层次放在中间,并给出了他自己的Patch:alternate 4-level page tables patches。Andi更想保持自己的PATCH, 他认为Nick不过是玩了改名的游戏,而且他的PATCH经过测试很稳定,快被合并到主线了,不宜再折腾。
不过Linus却表达了对Nick Piggin的支持,理由是Nick的做法conceptually least intrusive。毕竟作为Linux的扛把子,稳定对于Linus来说意义重大。
最终,不意外地,最后Nick Piggin的PATCH在v2.6.11版本中被合并入主线。在这种方案中,4级页表分别是:PGD -> PUD -> PMD -> PTE。
四、页式管理
4.1 分段机制存在的问题
分段,是指将程序所需要的内存空间大小的虚拟空间,通过映射机制映射到某个物理地址空间(映射的操作由硬件完成)。分段映射机制解决了之前操作系统存在的两个问题:
-
(1)地址空间没有隔离。
-
(2)程序运行的地址不确定。
不过分段方法存在一个严重的问题:内存的使用效率低。
分段的内存映射单位是整个程序;如果内存不足,被换入换出到磁盘的空间都是整个程序的所需空间,这会造成大量的磁盘访问操作,并且严重降低了运行速度。
事实上,很多时候程序运行所需要的数据只是很小的一部分,加入到内存的数据大小可能会很小,并没有必要整体的写入和写出。
分页机制解决了上面分段方法所存在的一个内存使用效率问题;其核心思想是系统为程序执行文件中的第x页分配了内存中的第y页,同时y页会添加到进程虚拟空间地址的映射表中(页表),这样程序就可以通过映射访问到内存页y。
4.2 分页存储的基本内容
分页的基本方法是将地址空间人为地等分成某一个固定大小的页;每一页大小由硬件来决定,或者是由操作系统来决定(如果硬件支持多种大小的页)。目前,以大小为4KB的分页是绝大多数PC操作系统的选择。
-
逻辑空间等分为页;并从0开始编号。
-
内存空间等分为块,与页面大小相同;从0开始编号。
-
分配内存时,以块为单位将进程中的若干个页分别装入。
关于进程分页. 当我们把进程的虚拟地址空间按页来分割,常用的数据和代码会被装在到内存;暂时没用到的是数据和代码则保存在磁盘中,需要用到的时候,再从磁盘中加载到内存中即可。
这里需要了解三个概念:
-
虚拟页(VP, Virtual Page),虚拟空间中的页;
-
物理页(PP, Physical Page),物理内存中的页;
-
磁盘页(DP, Disk Page),磁盘中的页。
虚拟内存的实现需要硬件的支持,从Virtual Address到Physical Address的映射,通过一个叫MMU(Memory Mangement Unit)的部件来完成。
五、分页机制支持
5.1 硬件分页支持
分页单元(paging unit)把线性地址转换成物理地址。其中的一个关键任务就是把所请求的访问类型与线性地址的访问权限相比较,如果这次内存访问是无效的,就产生一个缺页异常。
-
页:为了更高效和更经济的管理内存,线性地址被分为以固定长度为单位的组,成为页。页内部连续的线性地址空间被映射到连续的物理地址中。这样,内核可以指定一个页的物理地址和对应的存取权限,而不用指定全部线性地址的存取权限。这里说页,同时指一组线性地址以及这组地址包含的数据
-
页框:分页单元把所有的 RAM 分成固定长度的页框(page frame)(有时叫做物理页)。每一个页框包含一个页(page),也就是说一个页框的长度与一个页的长度一致。页框是主存的一部分,因此也是一个存储区域。区分一页和一个页框是很重要的,前者只是一个数据块,可以存放在任何页框或磁盘中。
-
页表:把线性地址映射到物理地址的数据结构称为页表(page table)。页表存放在主存中,并在启用分页单元之前必须由内核对页表进行适当的初始化。
5.2 常规的32bit分页
常规4KB分页,32位的线性地址被分成3个域
| Directory(目录) | Table(页表) | Offset(偏移量) |
|---|---|---|
| 最高10位 | 中间10位 | 最低12位 |
线性地址的转换分为两步完成,每一步都基于一种转换表,第一种转换表称为页目录表(page directory),第二种转换表称为页表(page table)。
每个活动的进程必须有一个页目录,但是却没有必要马上为所有进程的所有页表都分配 RAM,只有在实际需要一个页表时候才给该页表分配 RAM。
页目录项和页表项有同样的结构,每项都包含下面的字段:
| 字段 | 描述 |
|---|---|
| Present标志 | 如果被置为1,所指的页(或页表)就在主存中;如果该标志为0,则这一页不在主存中,此时这个表项剩余的位可由操作系统用于自己的目的。如果执行一个地址转换所需的页表项或页目录项中Present标志被清0,那么分页单元就把该线性地址转换所需的页表项或页目录项中Present标志被清0,那么分页单元就把该线性地址存放在控制寄存器cr2中,并产生14号异常:缺页异常。 |
| 包含页框物理地址最高20位的字段。由于每一个页框有4KB的容量,它的物理地址必须是4096的倍数,因此物理地址的最低12位总为0.如果这个字段指向一个页目录,相应的页框就含有一个页表;如果它指向一个页表,相应的页框就含有一页数据 | |
| Accessed标志 | 每当分页单元对相应页框进行寻址时就设置这个标志。当选中的页被交换出去时,这一标志就可以由操作系统使用。分页单元从来不重置这个标志,而是必须由操作系统去做 |
| Dirty标志 | 只应用于页表项中。每当对一个页框进行写操作时就设置这个标志。与Accessed标志一样,当选中的页被交换出去时,这一标志就可以由操作系统使用。分页单元从来不重置这个标志,而是必须由操作系统去做。 |
| Read/Write标志 | 含有页或页表的存取权限(Read/Write或Read)。 |
| User/Supervisor标志 | 含有访问页或页表所需的特权级。 |
| PCD和PWT标志 | 控制硬件高速缓存处理页或页表的方式。 |
| Page Size标志 | 只应用于页目录项。如果设置为1,则页目录项指的是2MB或4MB页框。 |
| Global标志 | 只应用于页表项。这个标志是在Pentium Pro中引入的,用来防止常用页从TLB高速缓存中刷新出去。只有在cr4寄存器的页全局启用(Page Global Enable, PGE)标志置位时这个标志才起作用。 |
正在使用的页目录的物理地址存放在控制寄存器CR3中。
了解了以上结构之后,我们看看如何从线性地址转换到物理地址的:
-
线性地址中的 Directory 字段决定页目录中的目录项,目录项指向适当的页表。
-
线性地址中的 Table 字段又决定页表的页表项,页表项含有页所在页框的物理地址。
-
线性地址中的 Offset 地段决定了页框内的相对位置,由于 offset 为 12 为,所以一页含有 4096 字节的数据。
Directory字段和Table字段都是10位长,因此页目录和页表都可以多达1024项。那么一个页目录可以寻址到高达1024*1024*4096=232个存储单元,这和32位地址所期望的一样。
5.3 物理地址扩展(PAE)分页机制和扩展分页(PSE)
处理器所支持的RAM容易受到连接到地址总线上的地址管脚树限制. 早期Intel处理器从80386到Pentium使用32位物理地址。
从理论上讲, 这样的系统可以使用高达2^32=4GB的RAM, 而实际上, 由于用户进程现行地址空间的需要, 4GB的虚拟地址按照1:3的比例划分给内核虚拟地址空间和进程虚拟地址空间. 则内核只能直接对1GB的线性地址空间进行寻址。
然而, 大型服务器需要大于4GB的RAM来同时运行数以钱计的进程, 所以必须扩展32位80x86架构所支持的RAM容量。
Intel通过在它的处理器上把管脚数从32增加到36满足这样的需要, 从Pentinum Pro开始, Intel所有处理器的寻址能力可达到2^36=64GB, 但是只有引入一种新的分页机制才能把32位现行地址转换为36位物理地址才能使用所增加的物理地址。
从Pentinum Pro处理器开始, Intel引入一种叫做物理地址扩展(Physical Address Extension, PAE)的机制.
从Pentium模型开始,80x86微处理器引入了扩展分页(externded paging),也叫页大小扩展[Page Size Extension], 它允许页框大小为4MB而不是4KB。扩展分页用于把大段连续的线性地址转换成相应的物理地址,在这种情况下,内核可以不用中间页表进行地址转换,从而节省内存并保留TLB项。
但是Linux并没有采用这种机制?
正如前面所述,通过设置页目录项的Page Size标志启用扩展分页功能。在这种情况下,分页单元把32位线性地址分成两个字段:
Directory:最高10位。
Offfset:其余22位。
扩展分页和正常分页的页目录项基本相同,
* Page Size标志必须被设置。
* 20位物理地址字段只有最高10位是有意义的。这是因为每一个物理地址都是在以4MB为边界的地方开始的,故这个地址的最低22位为0。
通过设置cr4处理器寄存器的PSE标志能使扩展分页与常规分页共存。
Intel为了支持PAE改变了分页机制
-
64GB的RAM被分成了2^24个页框, 页表项的物理地址字段从20位扩展到了24位. 因为PAE页表项必须包含12个标志位和24个物理地址位, 总数之和为36, 页表项大小从32位扩展到了64位, 结果, 一个4KB的页表项包含512个表项而不是1024个表项。
-
引入一个页目录指针表(Page Directory Pointer Table, PDPT)的页表新级别, 它由4个64位表项组成。
-
cr3控制寄存器包含一个27位的页目录指针表(PDPT)基地址字段. 因为PDPT存放在RAM的前4GB中, 并在32字节(2^5)的倍数上对其, 因此27位足以表示这种表的基地址。
-
当把线性地址映射到4KB的页时(页目录项中的PS标准清0), 32位线性地址将按照如下方式解释。
| 字段 | 描述 | 位数 |
|---|---|---|
| cr3 | 指向一个PDPT | crs寄存器存储 |
| PGD | 指向PDPT中4个项中的一个 | 位31~30 |
| PMD | 指向页目录中512项中的一个 | 位29~21 |
| PTE | 指向页表中512项中的一个 | 位20~12 |
| page offset | 4KB页中的偏移 | 位11~0 |
* 当把现行地址映射到2MB的页时(页目录项中的PS标志置为1), 32位线性地址按照如下方式解释。
| 字段 | 描述 | 位数 |
|---|---|---|
| cr3 | 指向一个PDPT | crs寄存器存储 |
| PGD | 指向PDPT中4个项中的一个 | 位31~30 |
| PMD | 指向页目录中512项中的一个 | 位29~21 |
| page offset | 2MB页中的偏移 | 位20~0 |
总之, 一旦cr3被设置, 就可能寻址高达4GB RAM, 如果我们期望堆更多的RAM进行寻址, 就必须在cr3中放置一个新值, 或改变PDPT的内容。
但是PAE的主要问题是线性地址仍然是32位长, 这就需要内核黑客用同一线性地址映射不同的RAM区。很显然, PAE并没有扩大进程的线性地址空间, 因为它只处理物理地址. 此外, 只有内核能够修改进程的页表, 所以在用户态下运行的程序不可能使用大于4GB的物理地址空间. 另一方面, PAE允许内核使用容量高达64GB的RAM, 从而显著的增加系统中的进程数目。
5.4 64位系统中的分页
32位处理器普遍采用两级分页。然而两级分页并不适用于采用64位系统的计算机。
原因如下 :
首先假设一个大小为4KB的标准页,4KB覆盖2^12个地址,所以offset字段是12位。如果我们现在决定仅仅使用64位中的48位来寻址(这个限制仍然能是我们自在地拥有256TB的寻址空间!),剩下的48-12=36位被分配给Table和Directory字段。如果我们决定为两个字段个预留18位,那么每个进程的页目录和页表都含有2^18个项,即超过256000个项。
由于这个原因,所有64位处理器的硬件分页系统都使用了额外的分页级别。使用的级别数量取决于处理器的类型。
| 平台名称 | 页大小 | 寻址使用位数 | 分页级别 | 线性地址分级 |
|---|---|---|---|---|
| alpha | 8KB | 43 | 3 | 10+10+10+13 |
| ia64 | 4KB | 39 | 3 | 9+9+9+12 |
| ppc64 | 4KB | 41 | 3 | 10+10+9+12 |
| x86_64 | 4KB | 48 | 4 | 9+9+9+9+12 |
5.5 硬件保护方案
与页和页表相关的特权级只有两个,因为特权由前面“常规分页”一节中所提到的User/Supervisor标志所控制。若这个标志为0,只有当CPL小于3(这意味着对于Linux而言,处理器处于内核态)时才能对页寻址;若该标志为1,则总能对页寻址。
此外,与段的3种存取权限(读,写,执行)不同的是,页的存取权限只有两种(读,写)。如果页目录项或页表项的Read/Write标志等于0,说明相应的页表或页是只读的,否则是可读写的。
1820

被折叠的 条评论
为什么被折叠?



