Linux内存寻址(二):分页机制

参考:https://www.cnblogs.com/bhlsheji/p/4868964.html

https://blog.csdn.net/shareCode/article/details/9024429

https://blog.csdn.net/ibless/article/details/81275009

CPU的页式内存管理单元,负责把一个线性地址,终于翻译为一个物理地址。从管理和效率的角度出发,线性地址被分为以固定长度为单位的组,称为页(page)。比如一个32位的机器。线性地址最大可为4G,能够用4KB为一个页来划分,这页,整个线性地址就被划分为一个tatol_page[2^20]的大数组,共同拥有2的20个次方个页。这个大数组我们称之为页目录。文件夹中的每个文件夹项,就是一个地址——相应的页的地址

这里注意到。这个total_page数组有2^20个成员,每一个成员是一个地址(32位机。一个地址也就是4字节)。那么要单单要表示这么一个数组,就要占去4MB的内存空间。

还有一类“页”,我们称之为物理页,或者是页框、页桢的。是分页单元把全部的物理内存也划分为固定长度的页框(也叫物理页)。每个页框包含一个页,,它的长度一般与内存页是一一相应的,页框是主存的一部分,因此也是个存储区域。区分一个页和页框,只是一个数据块,可以存放任何页框或磁盘中。

把线性地址转换成物理地址的数据结构叫做页表,页表存放在主存中,并在启用分页单元之前必须由内核对页表进行初始化。

从80386开始,所有的80x86都支持分页,它通过设置cr0寄存器的PG标志启动。当PG=0,时线性地址被解释为物理地址

1、分页单元中,页文件夹是唯一的,它的地址放在CPU的cr3寄存器中,是进行地址转换的開始点。万里长征就从此长始了。
2、每个活动的进程,由于都有其独立的相应的虚似内存(页文件夹也是唯一的),那么它也相应了一个独立的页文件夹地址。

扫描二维码关注公众号,回复: 9361869 查看本文章

——执行一个进程。须要将它的页文件夹地址放到cr3寄存器中,将别个的保存下来。

3、每个32位的线性地址被划分为三部份,页目录索引(10位):页表索引(10位):偏移(12位)
根据下面步骤进行转换:1、第一种转换表页目录表  2、第二种表称为页表

(1)从cr3中取出进程的页文件夹地址(操作系统负责在调度进程的时候。把这个地址装入相应寄存器);
(2)依据线性地址前十位。在数组中。找到相应的索引项,由于引入了二级管理模式。页文件夹中的项,不再是页的地址,而是一个页表的地址。(又引入了一个数组),页的地址被放到页表中去了。
(3)依据线性地址的中间十位。在页表(也是数组)中找到页的起始地址;
(4)将页的起始地址与线性地址中最后12位相加。得到终于我们想要的葫芦

使用二级模式的目的在于减少每个进程页表所需要的RAM的数量(出自《深入理解Linux内核》第三版51页)。如果使用简单的一级页表,那将需要高达2^20个页表(也就是在每项4个字节时,需要4MB的RAM,来表示每个进程的页表(如果进程使用全部4GB线性地址空间),即使一个进程并不使用那个范围的所有地址。二级模式值为进程实际使用那些虚拟内存去请求页表来减少内存容量。

对于减少使用内存数量的理解:1、进程使用4M空间,那么二级页表页目录占4k 页表占4k共 8k

2、其实是这样的,二级表只是从进程的角度来看,为进程节省了页表项(其实所有的页表存储空间增大了)。二级模式通过只为进程实际使用的那些虚拟内存区请求页表来减少页表,就是进程未使用的页暂时可以不用为其建立页表,因为如果使用一级页表的话,你就必须为所有的4G范围内分配页表,不能细分。每个活动进程必须有一个分配给它的页目录,不过没必要马上为进程的所有页表都分配ram,只有在进程实际需要一个页表时才给该页表分配ram,这样就提高了效率。

注:正在使用的页目录物理地址存放在控制寄存器cr3中。

注:linux进程的线性地址空间分成两部分:

            (1)0x00000000~0xbfffffff的线性地址,无论用户态和内核态的进程都可以寻址;

            (2)0xc0000000~0xffffffff的线性地址,只有内核态的进程可以寻址。

对于32位处理器来说,32位的线性地址被分成三部分,如下图。其中,最低12位表示偏移量,中间的10位表示页表项,最高10位表示页目录项。由线性地址转换成物理地址经过两部,也就是所谓的二级页表。

32位线性地址寻址过程如下:

说明:控制寄存器cr3中存放了页目录的物理地址,通过cr3寄存器可以找到页目录,而线性地址中的Directory部分决定页目录中的目录项,而页目录项中存放了要找的页表的物理基地址,再结合线性地址中的中间10位(Page Table部分),就可以找到页框的物理地址。一个页框大小为4096字节,线性地址中的Offset部分占12位,因此页框的物理地址结合线性地址Offset部分就可以找到页框中的任何一个字节。

问题来了,为什么要用二级页表而不使用以及页表,或者说使用二级页表比一级页表的优势在哪里?

(1)使用多级页表可以使得页表在内存中离散存储。多级页表实际上是增加了索引,有了索引就可以定位到具体的项。举个例子:比如虚拟地址空间大小为4G,每个页大小依然为4K,如果使用一级页表的话,共有2^20个页表项,如果每一个页表项占4B,那么存放所有页表项需要4M,为了能够随机访问,那么就需要连续4M的内存空间来存放所有的页表项。随着虚拟地址空间的增大,存放页表所需要的连续空间也会增大,在操作系统内存紧张或者内存碎片较多时,这无疑会带来额外的开销。但是如果使用多级页表,我们可以使用一页来存放页目录项,页表项存放在内存中的其他位置,不用保证页目录项和页表项连续。

(2)使用多级页表可以节省页表内存。使用一级页表,需要连续的内存空间来存放所有的页表项。多级页表通过只为进程实际使用的那些虚拟地址内存区请求页表来减少内存使用量(出自《深入理解Linux内核》第三版51页)。举个例子:比如一个进程只是用4MB内存空间。对于一级页表,我们需要4M空间来存放页表,然后可以找到进程真正使用的4M内存空间。但是如果使用二级页表的话,一个页目录项可以定位4M内存空间,存放一个页目录项占4K,还需要一页用于存放进程使用的4M(4M=1024*4K,也就是用1024个页表项可以映射4M内存空间)内存空间对应的页表,总共需要4K+4K=8K来存放进程使用的这4M内存空间对应页表和页目录项,这比使用一级页表节省了很多内存空间。

那么使用多级页表比使用以及页表有没有什么劣势呢?

当然是有的。比如:使用以及页表时,读取内存中一页内容需要2次访问内存,第一次是访问页表项,第二次是访问要读取的一页数据。但如果是使用二级页表的话,就需要3次访问内存了,第一次访问页目录项,第二次访问页表项,第三次访问要读取的一页数据。访存次数的增加也就意味着访问数据所花费的总时间增加。

扩展分页

从pentium开始x86引入了扩展分页, 它使页框为4MB而不是4KB, 内核就用不到页表项, 只需要页目录就可以访问全部物理地址,从而节省了内存.

扩展分页机制和正常分页的不同在于, Page size标志必须设置成支持4MB, 由于每个物理地址都是4MB对齐, 所以地址的最低22位都为0.

通过设置cr4寄存器的PSE能使扩展分页和常规分页共存.

常规分页举例:

假设内核给一个进程分配了0x20000000-0x2000ffff, 该空间为2 ^ 16一共32KB. 高10位为directory = 0x80, 页表范围从0 - 15, 其余页表项均无效.

假设该进程要访问0x2000e002则, directory 字段选择第0x80项,  pg tbl字段0x0e选择第0x0e表项此表项指向了该页的页框.

最后offset字段0x2用于在页框中读偏移量位0x2中的字节. 若0x0e表项的present设为0则分页单元在线性地址转换的同时产生一个缺页异常.

PAE分页:

intel为了处理大于4GB的RAM采用了PAE, 将32根pin增加到36根, 从而扩大了寻址能力通过设置cr4寄存器的PAE标志来激活, 激活后页目录项的页大小标志PS会使用大尺寸页(2MB).

高达64GB的寻址导致RAM被分为2 ^ 24个页框, 页表项的物理地址字段从20变成了24位, 页表项大小从32位扩展为64位, 结果导致

1个4KB的页表包含了512个表项而不是1024个表项.

该机制引入了一个PDPT(page directory pointer table), 它由4个64位表项组成

cr3控制寄存器包含了PDPT的基地址字段, 由于PDPT存在RAM的前4GB, 并且在32字节对齐, 所以用27位来表示

硬件高速缓存cache

对比的是物理地址高位

TLB

多处理器中,每个cpu都有自己的TLB,这叫做该cpu的本地TLB.与硬件高速缓存相反,TLB中的对应项不必同步,因为运行在现有CPU上的进程可以使用同一线性地址与不同物理地址发生联系。

当cpu的cr3控制器被修改是,硬件自动使本地TLB种所有想都无效,这是因为新的一组页表被启用而TLB指向的是旧数据

参考:

    深入理解linux内核

发布了13 篇原创文章 · 获赞 2 · 访问量 3273

猜你喜欢

转载自blog.csdn.net/wangyangzhizunwudi/article/details/100144333
今日推荐