(2021年11月18日打卡第十一天)
打卡第十一天:06 | 虚幻与真实:程序中的地址如何转换?
06 | 虚幻与真实:程序中的地址如何转换?
学习本节,搞清虚拟地址和物理地址的关系和转换机制。
1、多程序并发会带来哪些问题
内存中有两个程序,程序A和程序B各自运行一秒钟:
- 谁来保证程序 A 跟程序 B 没有内存地址的冲突?
- 怎样保证程序 A 跟程序 B 不会互相读写各自的内存空间?
- 如何解决内存容量问题?
- 如果不只程序 A、B,还可能有程序 C、D、E、F、G……它们分别由不同的公司开发,而每台计算机的内存容量不同。。。。。。
要想完美地解决以上最核心的 4 个问题,一个较好的方案是:让所有的程序都各自享有一个从 0 开始到最大地址的空间,这个地址空间是独立的,是该程序私有的,其它程序既看不到,也不能访问该地址空间,这个地址空间和其它程序无关,和具体的计算机也无关。
2、MMU的工作流程是怎样的
把虚拟地址空间和物理地址空间都分成同等大小的块,也称为页,按照虚拟页和物理页进行转换。一个虚拟页可以对应到一个物理页,由于页大小一经配置就是固定的,所以在地址关系转换表中,只要存放虚拟页地址对应的物理页地址就行了。
(32 位地址空间下,假设页的大小为4KB=
2
12
2^{12}
212,那么要管理4GB 虚拟地址的地址关系转换表的条目数为:
2
32
/
2
12
=
2
20
=
1
M
B
2^{32}/2^{12}=2^{20}=1MB
232/212=220=1MB,也就是说32 位地址空间下一共有1MB个这么多的页,每个条目是1个地址占4Byte,所以地址关系转换表共占
1
M
B
∗
4
=
4
M
B
1MB*4=4MB
1MB∗4=4MB,这也是为什么我们只有512M或2G内存,却开了那么多exe也没有问题的原因)
- 地址关系转换表(页表)是分级的,总体分为三个部分:一个顶级页目录,多个中级页目录,最后才是页表,逻辑结构图如下:
从上面可以看出,一个虚拟地址被分成从左至右四个位段。
- 第一个位段索引顶级页目录中一个项,该项指向一个中级页目录,然后用第二个位段去索引中级页目录中的一个项,该项指向一个页目录,再用第三个位段去索引页目录中的项,该项指向一个物理页地址,最后用第四个位段作该物理页内的偏移去访问物理内存。这就是 MMU 的工作流程。
- (四个位段这种方案类似windows中的2 9 9 12分页方案)
- (假设我们有一个虚拟地址0x12345678,按照上图四个位段的分法结合windows中的29912分页方案,该地址的分法为:0001 0010 0011 0100 0101 0110 0111 1000
- (00 010010001 101000101 0110 0111 1000
- (0 91 145 678)
- (再通过cr3中的物理地址,按照文中所述MMU的工作流程去寻址0x12345678对应的物理地址)
3、了解保护模式下的分页方案
分页大小的不同,会导致虚拟地址位段的分隔和页目录的层级不同。
- 保护模式下的分页——4KB 页
该分页方式下,32 位虚拟地址被分为三个位段:页目录索引、页表索引、页内偏移,只有一级页目录,其中包含 1024 个条目 ,每个条目指向一个页表,每个页表中有 1024 个条目。其中一个条目就指向一个物理页,每个物理页 4KB。这正好是 4GB 地址空间。
(三个位段这种方案类似windows中的10 10 12分页方案)
页目录项、页表项都是 4 字节 32 位,1024 个项正好是 4KB(一个页),因此它们的地址始终是 4KB 对齐的,所以低 12 位才可以另作它用,形成了页面的相关属性,如是否存在、是否可读可写、是用户页还是内核页、是否已写入、是否已访问等。
(因为每个页的地址都是4KB对齐,低 12 位也就是
2
12
=
4
K
B
2^{12}=4KB
212=4KB,所以寻址一个页面只需要32位地址前面的20位就够了)
- 保护模式下的分页——4MB 页
该分页方式下,32 位虚拟地址被分为两个位段:页表索引、页内偏移,只有一级页目录,其中包含 1024 个条目。其中一个条目指向一个物理页,每个物理页 4MB,正好为 4GB 地址空间。
4MB 大小的页面下,页表项还是 4 字节 32 位,但只需要用高 10 位来保存物理页面的基地址就可以。因为每个物理页面都是 4MB,所以低 22 位始终为 0,为了兼容 4MB 页表项低 8 位和 4KB 页表项一样,只不过第 7 位变成了 PS 位,且必须为 1,而 PAT 位移到了 12 位。
4、了解长模式下的分页方案
长模式下的分页大小通常也有两种,4KB 大小的页和 2MB 大小的页。
- 长模式下的分页——4KB 页
该分页方式下,64 位虚拟地址被分为 6 个位段,分别是:保留位段,顶级页目录索引、页目录指针索引、页目录索引、页表索引、页内偏移,顶级页目录、页目录指针、页目录、页表各占有 4KB 大小,其中各有 512 个条目,每个条目 8 字节 64 位大小,如下图所示:
- (6个位段这种方案类似windows中的9 9 9 9 12分页方案)
- 长模式下的分页——2MB 页
在这种分页方式下,64 位虚拟地址被分为 5 个位段 :保留位段、顶级页目录索引、页目录指针索引、页目录索引,页内偏移,顶级页目录、页目录指针、页目录各占有 4KB 大小,其中各有 512 个条目,每个条目 8 字节 64 位大小。
5、怎么开启MMU?
- 开启 MMU,步骤如下:
- 使 CPU 进入保护模式或者长模式。
- 准备好页表数据,这包含顶级页目录,中间层页目录,页表,假定我们已经编写了代码,在物理内存中生成了这些数据。
- 把顶级页目录的物理内存地址赋值给 CR3 寄存器。
mov eax, PAGE_TLB_BADR ;页表物理地址
mov cr3, eax
- 设置 CPU 的 CR0 的 PE 位为 1,这样就开启了 MMU。
;开启 保护模式和分页模式
mov eax, cr0
bts eax, 0 ;CR0.PE =1
bts eax, 31 ;CR0.PG = 1
mov cr0, eax