一个逻辑地址由两部分组成,段标识符:段内偏移量,。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号,后面3位包含一些硬件细节:
最后两位涉及权限检查
索引号——直接可以理解成数组下标,它是“段描述符”的索引,段描述符具体地址描述了一个段,这样,可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就代表了一个段的描述,每一个段描述符由8个字节组成:
上面的描述符虽然很复杂,但是我们需要利用一个数据结构来定义它,不过,在这里只需要关心Base字段,它描述了一个段开始位置的线性地址。
Intel设计的本意是,一些全局的段描述符,就放在“全局段描述表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述表(LDT)”中,在段选择符的T1字段中=0,表示用GDT,=1,表示用LDT。
GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。如下可看出:
首先,给定一个完整的逻辑地址[段选择符:段偏移地址]
1.
2.
3.
CPU页式内存管理
CPU的页式内存管理单元,负责把一个线性地址,最终翻译为一个物理地址。从管理和效率的角度出发,线性地址被分为以固定长度为单位的组,成为页(page),例如一个32位的机器,线性地址最大可为4G,可以用4KB位一个页来划分,这个页,作为一个线性地址就被划分为一个total_page[2^20]的大数组,共有2的20个次方个页,这个大数组我们称之为页目录。目录中的每一个目录项,就是一个地址一一对应的页的地址。
另一类“页“,我们称之为物理页,或者是页框,是分页段元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。
这里注意到,total_page数组有2^20个成员,每个成员是一个地址(32位机,一个地址也就是4字节),那么要单单表示这么一个数组,就要占去4MB的内存空间,为了节省空间,引入了一个二级管理模式的机器来组织分页单元:
如上图:
1.
2.
3.
1) 从cr3中取出进程的页目录地址(操作系统负责在调度进程的时候,把这个地址装入对应寄存器);
2) 根据线性地址前十位,在数组中,找到对应的索引项,因为引入了二级管理模式,页目录中的项,不再是页的地址,而是一个页表的地址。(又引入了一个数组),页的地址被放到页表中去了。
3) 根据线性地址的中间十位,在页表(也是数组)中找到页的起始地址;
4) 将页的起始地址与线性地址中最后12位相加,得到最终我们想要的内存块
页置换算法(主要是查看是否缺页或者调度)
Win32通过一个两层的表结构来实现地址映射,因为每个进程都拥有私有的4G的虚拟内存空间,相应的,每个进程都有自己的层次表结构来实现其地址映射。
如下图所示(注下图中的页目录项和页表项的大小应该是4个字节,而不是4kB):
具体的例子:
假设一个线程正在访问一个指针(Win32的指针指的就是虚拟地址)指向的数据,此指针指为0x2A8E317F,下图表示了这一个过程:
0x2A8E317F的二进制写法为0010101010_0011100011_000101111111,为了方便我们把它分为三个部分。
首先按照0010101010寻址,找到页目录项。因为一个页目录项为4KB,那么先将0010101010左移两位,001010101000(0x2A8),用此下标找到页目录项,然后根据此页目录项定位到下一层的某个页表。
然后按照0011100011寻址,在上一步找到页表中寻找页表项。寻址方法与上述方法类似。找到页表项后,就可以找到对应的物理内存页。
最后按照000101111111寻址,寻找页内偏移。
段页存储管理
产生原因
分页式:等分内存,但是需要仔细设计页面的大小,灵活性很低
分段式:支持了用户内存观点,反映了程序结构,但是粒度一般很大,碎片问题很明显
段页式存储管理优点:
对于用户来讲,按照段的逻辑划分程序
对于系统来讲,按页划分每一段
逻辑地址
管理
段表——记录了每一段的页表开始地址和页表长度;
页表——记录了逻辑页号与内存块号的对应关系,每一段有一个,一个程序可能有多个页表
空块管理:同页式管理
分配:同页式管理
硬件支持
1)
2)
3)