内存的分页管理方式解释

今天在思考一个可能由page table引发的Linux操作系统内存报警问题时发现基础知识缺失的太多,因此找了几本操作系统相关的书复习了一下,在这里记下来。

首先上一幅32位寻址空间的虚拟地址结构图:(仅适用于一级页表,页面大小为4KB)

前12位表示页内偏移量,后20位表示页号,可寻址2^20=1M个页,假如页面大小为4KB,那么就能寻址4GB的虚拟地址。

那么这样一个32位的虚拟地址是如何转换为物理地址的呢?

下图说明了一切:

图1.分页系统的地址变换机构 --摘自《计算机操作系统》汤小丹版

寻址步骤解析:

1.首先假设虚拟地址为A,页面大小为L,则可以通过以下公式迅速计算出相应的页号P和页内偏移量d:

P=INT [A/L] --除法取整
d=A%L  --求余

2.得到P和d之后,使用P与页表寄存器中的寄存的页表始址(一个物理地址),进行逻辑运算得到页表项的具体位置。(其实就是通过页表始址找到页表页,再在里边找到页号)

3.在此页表项中取到后半截的块号--通过块号得到块的物理始址,通过这个物理始址与页内偏移量相加得到真正的物理地址。

整个过程很简单,就两个加法,两次寻址。

举例说明:

一个页表的结构如下所示:(这里省了标识页表的始址,直接给了页表的内容)

页号    块号
0        3
1        2
2        0
3        1

 

假设有一个虚拟地址0x000B,页面大小为4KB(0x1000),那么:

1.P=0x000B/0x1000=0; d=0x000B%0x1000=0x000B;因此页号为0,页内偏移量为0x000B

2.通过与寄存器的逻辑运算找到如上页面的0页号,发现0号对应物理块号是3

3.3*0x1000+0x000B=0x300B

因此真实的物理地址就是0x300B。

拓展知识:

由于页表空间必须是连续的内存地址,这样的开销在32位分页系统中几乎是不可接受的,因此我们一般采用如下两种办法解决此问题:

1.多级页表,即页表的页表,这样页表可以离散的分布于内存之中,不必是连续内存,64位寻址空间的分页系统常见3级页表,32位的常见两级页表。

2.只缓存一部分的页表,其他页表部分在外存中,根据需要调入,这样解决了页表空间占用的问题;对于多级页表,其外部页表必须调入内存,页表则可以只缓存一部分。

现在常见的64位操作系统一般采用多级页表的方式,大多数为3级页表,其实就是页表3级索引,因此CPU多出了2个页表寄存器来记录多级页表的物理始址,但是本质与一级页表寻址方式类似。

自2.6.11内核版本以来,Linux采用4级页表的分页方式(9+9+9+12)。

在64位分页系统中,由于可寻址范围已经大大扩展,因此所有的页目录和页表都已被存入内存。

对于用户空间和内核空间,以及高端内存、低端内存还有线性映射、非线性映射等概念,在64位分页系统中应当全部舍弃,除非你是研究内核的。

最后再放一个段页式逻辑地址-->线性地址(虚拟地址)-->物理地址的转换图:

 

posted @ 2017-05-30 22:41  realcp1018  阅读(1363)  评论(0编辑  收藏  举报