Windows 内存管理方法(三)

时间:2022-09-25 14:28:11



一个逻辑地址由两部分组成,段标识符:段内偏移量,。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号,后面3位包含一些硬件细节:

Windows 内存管理方法(三)

最后两位涉及权限检查

索引号——直接可以理解成数组下标,它是“段描述符”的索引,段描述符具体地址描述了一个段,这样,可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就代表了一个段的描述,每一个段描述符由8个字节组成:


Windows 内存管理方法(三)

 

上面的描述符虽然很复杂,但是我们需要利用一个数据结构来定义它,不过,在这里只需要关心Base字段,它描述了一个段开始位置的线性地址。

Intel设计的本意是,一些全局的段描述符,就放在“全局段描述表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述表(LDT中,在段选择符的T1字段中=0,表示用GDT=1,表示用LDT

GDT在内存中的地址和大小存放在CPUgdtr控制寄存器中,而LDT则在ldtr寄存器中。如下可看出:


Windows 内存管理方法(三)

 

首先,给定一个完整的逻辑地址[段选择符:段偏移地址]

1.       看段选择符的T1=0还是1,知道当前要转换的是GDT中的段,还是LDT中的段,再根据相应的寄存器,得到其地址和大小,我们就有一个数组了。

2.       拿出段选择符中的前13位,可以在这个数组中,找到对应的段描述符,这样,它的base,即基地址就知道了。

3.       Base+offset,就是要转换的线性地址了。

 

 

CPU页式内存管理

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

另一类“页“,我们称之为物理页,或者是页框,是分页段元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。

这里注意到,total_page数组有2^20个成员,每个成员是一个地址(32位机,一个地址也就是4字节),那么要单单表示这么一个数组,就要占去4MB的内存空间,为了节省空间,引入了一个二级管理模式的机器来组织分页单元:


Windows 内存管理方法(三)

 

如上图:

1.       分页单元中,页目录是唯一的,它的地址放在CPUcr3就成年期中,是进行地址转换的开始点;

2.       每一个活动的进程,因为都有其独立对应的虚拟内存(页目录也是唯一的),那么它也对应了而一个独立的页目录地址。(运行一个进程,需要将它的页目录地址放到cr3寄存器中,将别的进程的地址保存);

3.       每一个32位的线性地址被划分为三部分,面目录索引(10位):页表索引(10位):偏移(12位)。依据以下的步骤进行转换:

1 cr3中取出进程的页目录地址(操作系统负责在调度进程的时候,把这个地址装入对应寄存器);

2 根据线性地址前十位,在数组中,找到对应的索引项,因为引入了二级管理模式,页目录中的项,不再是页的地址,而是一个页表的地址。(又引入了一个数组),页的地址被放到页表中去了。

3 根据线性地址的中间十位,在页表(也是数组)中找到页的起始地址;

4 将页的起始地址与线性地址中最后12位相加,得到最终我们想要的内存块

 

 

页置换算法(主要是查看是否缺页或者调度)

                 


Win32通过一个两层的表结构来实现地址映射,因为每个进程都拥有私有的4G的虚拟内存空间,相应的,每个进程都有自己的层次表结构来实现其地址映射。

      第一层称为页目录,实际就是一个内存页,Win32的内存页有4KB大小,这个内存页以4个字节分为1024项,每一项称为“页目录项”(PDE)
     
 第二层称为页表,这一层共有1024个页表,页表结构与页目录相似,每个页表也都是一个内存页,这个内存页以4KB的大小被分为1024项,页表的每一项被称为页表项(PTE),易知共有1024×1024个页表项。每一个页表项对应一个物理内存中的某一个“内存页”,即共有1024×1024个物理内存页,每个物理内存页为4KB,这样就可以索引到4G大小的虚拟物理内存。
如下图所示(注下图中的页目录项和页表项的大小应该是4个字节,而不是4kB)



Windows 内存管理方法(三)

 

      Win32提供了4GB大小的虚拟地址空间。因此每个虚拟地址都是一个32位的整数值,也就是我们平时所说的指针,即指针的大小为4B。它由三部分组成,如下图:

 Windows 内存管理方法(三)

      这三个部分的第一部分,即前10位为页目录下标,用来寻址页目录项,页目录项刚好1024个。找到页目录项后,找对页目录项对应的的页表。第二部分则是用来在页表内寻址,用来找到页表项,共有1024个页表项,通过页表项找到物理内存页。第三部分用来在物理内存页中找到对应的字节,一个页的大小是4KB12位刚好可以满足寻址要求。

具体的例子:
假设一个线程正在访问一个指针(Win32的指针指的就是虚拟地址)指向的数据,此指针指为0x2A8E317F,下图表示了这一个过程:

Windows 内存管理方法(三)

 

0x2A8E317F的二进制写法为0010101010_0011100011_000101111111,为了方便我们把它分为三个部分。
首先按照0010101010寻址,找到页目录项。因为一个页目录项为4KB,那么先将0010101010左移两
位,0010101010000x2A8),用此下标找到页目录项,然后根据此页目录项定位到下一层的某个页表。

然后按照0011100011寻址,在上一步找到页表中寻找页表项。寻址方法与上述方法类似。找到页表项后,就可以找到对应的物理内存页。
最后按照000101111111寻址,寻找页内偏移。
     
 上面的假设的是此数据已在物理内存中,其
实判断访问的数据是否在内存中也是在地址映射过程中完成的。Win32系统总是假设数据已在物理内存中,并进行地址映射。页表项中有一位标志位,用来标识包含此数据的页是否在物理内存中,如果在的话,就直接做地址映射,否则,抛出缺页中断,此时页表项也可标识包含此数据的页是否在调页文件中(外存),如果不在则访问违例,程序将会退出,如果在,页表项会查出此数据页在哪个调页文件中,然后将此数据页调入物理内存,再继续进行地址映射。为了实现每个进程拥有私有4G的虚拟地址空间,也就是说每个进程都拥有自己的页目录和页表结构,对不同进程而言,即使是相同的指针(虚拟地址)被不同的进程映射到的物理地址也是不同的,这也意味着在进程之间传递指针是没有意义的。

 

 Windows 内存管理方法(三)

 

 


段页存储管理

产生原因

分页式:等分内存,但是需要仔细设计页面的大小,灵活性很低

分段式:支持了用户内存观点,反映了程序结构,但是粒度一般很大,碎片问题很明显

段页式存储管理优点:

对于用户来讲按照段的逻辑划分程序

对于系统来讲,按页划分每一段

逻辑地址

                   

管理

段表——记录了每一段的页表开始地址和页表长度;

页表——记录了逻辑页号与内存块号的对应关系,每一段有一个,一个程序可能有多个页表

空块管理:同页式管理

分配:同页式管理

硬件支持

1)  段表始址寄存器

2)  段表长度寄存器

3)  相联存储器(快表)