保护模式进阶

本章为之前学习《操作系统真相还原》一书时候的笔记,内容为第五章

获取物理内存容量

学习Linux获取内存的方法

在Linux 2.6内核中,获取内存是通过detect_memory函数来获取内存容量的,其函数本质上通过BIOS的0x15中断实现,0x15中断的三个子功能:

  • EAX=0xE820,遍历主机上全部内存
  • AX=0xE801,分别检测低15MB和16MB~4GB的内存,最大支持4GB
  • AH=0x88,最多检测出64MB内存,实际内存超过容量也按64MB返回

BIOS中断位实模式下的方法,因此要在进入保护模式前调用,如果一种方法失败会尝试另一种方法,如果都失败了则停止运行

利用BIOS中断0x15子功能0xe820获取内存

0xe820子功能返回的内存信息较丰富,使用地址范围描述符来描述:

image.png

type字段用来描述这段内存的类型和用途:

image.png

同时,该子功能的中断参数说明:

image.png

利用BIOS中断0x15子功能0xe801获取内存

该子功能最大只能识别4GB内存

说明:

image.png

利用BIOS中断0x15子功能0x88获取内存

该子功能最简单,最多只能识别64MB大小内存

使用说明:

image.png

启用内存分页机制

内存为什么要分页

如下所示:

image.png

当出现上述的内存问题时,有以下两个办法:

  • 等待进程C运行结束
  • 将进程A或C的部分内存换出到硬盘

内存换出的过程:

  • 如果该描述符中P位为1,表示该段在内存中存在。访问过该段后, CPU 将段描述符中的A位置,表示近来刚访问过该段。相反,如果P位为0,说明内存中并不存在该段,这时候 CPU 将会抛出个 NP (段不存在)异常,转而去执行中断描述符表中 NP 异常对应的中断处理程序,此中断处理程序是操作系统负责提供的,该程序的工作是将相应的段从外存(比如硬盘)中载入到内存,并将段描述符的P位置1,中断处理函数结束后返回, CPU 重复执行这个检查,继续查看该段描述符的P位,此时已经为1了,在检查通过后,将段描述符的A位置 1
  • 段描述符的A位主要用于统计一个周期内段的访问频率

内存换出的办法的缺点为,受硬盘剩余空间影响且整个过程非常耗时

归根结底,造成这种困扰的原因就是,在只分段的情况下,CPU认为线性地址等于物理地址,线性地址是由编译器编译出来的,它本身是连续的,所以物理地址也必须要连续才行,但我们可用的物理地址不连续

由此,我们首先要做的是解除线性地址与物理地址一一对应的关系,然后将它们的关系重新建立。通过某种映射关系,可以将线性地址映射到任意物理地址

一级页表

在不打开分页机制的情况下,内存访问是直接根据段基址+偏移地址进行的:

img

但是,如果打开了分页机制,段基址+偏移地址得到的就是虚拟地址,需要在页表中查找到相应的物理地址才能拿来使用:

image.png

分页机制的作用有两方面:

  • 将线性地址转换成物理地址
  • 用大小相等的页代替大小不等的段

如图所示:

image.png

32位地址可以分成两部分,内存块数量和内存块尺寸:

image.png

如下所示为一级页表模型:

image.png

由于页大小是4KB,所以页表项中的物理地址都是4k的整数倍,故用十六进制表示的地址,低3位都是0

地址的低12位可用于表示在页内的偏移,用于页内寻址,高20位用于页表项

当程序中给出一个线性地址时,由页部件分析线性地址,在页表中检索到物理地址

拿mov ax, [0x1234]举例,一级页表的地址转换过程如下:

image.png

二级页表

当前操作系统用的一般都是二级页表

建立二级页表的原因为:

一级页表必须要提前建立好,而每个进程都有自己的页表,进程一多,光是页表就要占领很大一部分空间

标准页的尺寸为4KB,一级页表是将4GB内存分成1M个标准页放到一张页表中,二级页表是将这1M个标准页平均放置1K个页表中,,每个页表含有1K个页表项,页表项大小是4字节,如图所示:

image.png

在二级页表中,虚拟地址到物理地址的转化也与一级页表不同

我们要定位到某个物理页,首先要在页目录表中找到其所属的页表,所以首先使用高10位二进制(32-22位)来索引页表

然后用中间10位(21-12位)来在页表中索引物理页,最后用低12位(11-0位)用于页内偏移量

例如mov ax, [0x12345678],地址转换过程如图所示:

image.png

搞清楚过程之后看下页表项和页目录项,由于页大小为4KB,所以低12位为000,因此可以拿来他用

image.png

一些属性的解释如下:

  • P,Present,即为存在位,若1表示该页存在于物理内存中,为0表示不存在
  • RW,Read/Write,意为读写位,若1表示可读可写,若0表示可读不可写
  • US,User/Supervisor,意为普通用户/超级用户位,1表示处于User级,任意级别特权(0,1,2,3)都可以访问该页,若为0表示处于Supervisor级,只允许特权级为0,1,2的程序访问
  • PWT,Page-level Write-Through,意为页级通写位,也称页级写透位,为1表示此项采用通写方式,该页不仅是普通内存,还是高速缓存
  • PCD,Page-level Cache Disable,意为页级高速缓存禁止位,为1表示该页启用高速缓存
  • A,Accessed,意为访问位,为1表示被CPU访问过了
  • D,Dirty,意为脏页位,当CPU对一个页面执行写操作时,就会设置对应页表项的D位为1
  • PAT,意为页属性表位,能够在页面一级的粒度上设置内存属性
  • G,Global,意为全局位,为1表示全局页
  • AVL,Available位,表示可用

启用分页机制,我们需要按顺序做好三件事:

  • 准备好页目录表及页表
  • 将页表地址写入控制寄存器cr3
  • 寄存器cr0的PG位置1