地址转换(Address Translation)
- 内存需要被分成固定大小的
页(Page)
- 然后再通过
虚拟内存地址(Virtual Address)
到物理内存地址(Physical Address)
的地址转换(Address Translation)
- 才能到达实际存放数据的物理内存位置
简单页表
页表的概念
- 想要把虚拟内存地址,映射到物理内存地址,最直观的办法,就是来建一张映射表
- 这个映射表,能够实现虚拟内存里面的页,到物理内存里面的页的一一映射,这个映射表,在计算机里,叫做
页表(Page Table)
页号 && 偏移量
- 页表这个地址转换的办法, 会把一个内存地址分成
页号(Directory)
和偏移量(Offset)
- 例子
-
前面的高位,就是内存地址的页号
-
后面的低位,就是内存地址里面的偏移量
-
做地址转换的页表,只需要保留虚拟内存地址的页号和物理内存地址的页号之间的映射关系即可
-
同一个页里面的内存,在物理层面是连续的。以一个页的大小是4K字节(4KB)为例,我们需要20位的高位,12位的低位
- 12位低位做偏移量,2的12次方=4096=4kb
-
32位的内存地址空间,页表一共需要2 ^ 20个到物理页号的映射关系
- 这个存储关系,就好比2 ^ 20 大小的数组
- 一个页号是完整的32位的4字节(Byte),这样一个页表就需要4MB的空间
- 2 ^ 20 * 4 = 4MB
- 总结
- 把虚拟内存地址,切分成页号和偏移量的组合
- 从页表里面,查询出虚拟页号,对应的物理页号
- 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址
多级页表
栈 && 堆内存分布
- 在整个进程的内存地址空间,通常是"两头实,中间空"
- 栈:在程序运行的时候,内存地址
从顶往下
,不断分配占用的栈空间 - 堆:内存地址则
从底部往上
,是不断分配占用的
4级页表
-
同样一个虚拟内存地址,偏移量的部分和上面简单页表一样不变,但是原先的页号部分,把它拆成四段,从高到低,分成4级到1级
-
运用流程
- 一个进程有1个4级页表。先通过4级页表索引,找到4级页表里面对应的
条目(Entry)
- 这个条目里存放的是一张3级页表所在的位置
- 4级页表里面的每一个条目,都对应一张3级页表,所以可以有多张3级页表
- 找到对应这张3级页表之后,用3级别索引去找到对应的3级索引的条目
- 3级别索引的条目再会指向一个2级页表
- 同样的,2级页表可以用2级索引指向一个1级页表
- 而最后一层的1级别页表里面的条目,对应的数据内容就是物理页号
- 在拿到了物理页号之后,同样可以用"页号 + 偏移量"的方式,来获取最终的物理内存地址
- 一个进程有1个4级页表。先通过4级页表索引,找到4级页表里面对应的
页表树(Page Table Tree)
-
因为虚拟内存地址分布的连续性,树的第一层节点的指针,很多就是空的,也就是不需要对应对应的子树
- 如果普通页表是数组结构,就必须是连续的
- 所以32位地址占用4MB空间,采用页表树,就可以把未占用的空间设置为null,从而节约出未占用的空间
-
所谓不需要子树,其实就是不需要对应的2级,3级的页表
-
找到最终最终的物理页号,就好像通过一个特定的访问路径,走到树最底层的叶子节点
-
空间测算
- 例子1
- 以这样的分成4级的多级页表,每一级如果都用5个比特(位),那么每一张某1级别的页表,只需要2^5=32个条目
- 如果每个条目还是4个字节,那么一共需要128个字节
- 而一个1级索引表,对应32个4kb的也就是128kb的大小,一个填满的2级索引表,对应的就是32个1级别索引表,也就是4MB大小
- 例子2
- 一个进程如果占用了8MB的内存空间,分成了2个4MB的连续空间
- 栈空间4M,堆空间4M (最上最下两部分)
- 它一共需要2个独立的,填满的2级别索引表,也就是意味着64个1级别索引表,2个独立的3级索引表,1个4级索引表引表
- 64(1级) + 2(2级别) + 2 (3级别) + 1(4级) = 69
- 一共需要69个索引表,每个128字节,大概9kb,比如4MB来说,只有1/500
- 每个索引表2^5=32个条目
- 每个条目中存储地址: 32位地址,8位一个Byte(字节),占4B空间, 总共 69 * 32 * 4B = 8832B 约等于9kb
- 一个进程如果占用了8MB的内存空间,分成了2个4MB的连续空间
- 例子1