如何支持巨大的虚拟地址空间
- 📖1. 为什么要支持巨大的虚拟地址空间
- 📖2. 交换空间
- 📖3. 存在位
- 📖4. 页错误
- 📖5. 为了处理页错误,操作系统大致做了什么?
- 📖6. 页面换出
- 📖7. 当程序从内存中读取数据会发生什么?
- 📖8. 页面置换什么时候真正发生
📖1. 为什么要支持巨大的虚拟地址空间
为什么我们要为进程支持巨大的虚拟地址空间呢?
为了方便和易用性,有了巨大的地址空间,你不必担心程序的数据和代码是否有空间存储,只需自然的编写代码,根据需要分配内存.
📖2. 交换空间
我们需要在硬盘上开辟一部分空间用于物理页的移入和移出,在操作系统中,一般这样的空间称为交换空间(swap space).因为我们将内存中的页交换到其中,并在需要的时候又交换回去.
上图中,可以看到一个4
页的物理内存和8
页的交换空间(在磁盘上),有4
个进程,进程0
、进程1
、进程2
都只有一部分有效页在内存中,剩下的在硬盘的交换空间中,进程3
的所有页都被交换到了磁盘上,因此它目前没有运行,有一块交换空间是空闲的,通过这个小例子,你应该能够看出,使用交换空间是如何让系统假装比实际物理内存更大.
📖3. 存在位
现在我们在硬盘上有一部分交换空间,需要在系统中增加一些机制,来支持从硬盘交换页.
让我们先回想一下内存引用会发生什么?
正在运行的进程生成虚拟地址(用于获取指令或访问数据),硬件将其转换为物理地址,再从内存中获取所需数据.
硬件首先从虚拟地址获得VPN
(虚拟页号),检查TLB
是否命中,如果命中,则获得最终的物理地址并从内存中取回,它不需要额外的内存访问.
如果在TLB
中找不到VPN
(即TLB
未命中),则硬件在内存中查找页表(使用页表基址寄存器),并使用VPN
查找该页的页表项(PTE
)作为索引,如果页有效且存在于物理内存中,则硬件从PTE
中获得PFN
,将其插入TLB
,并重试该指令,这次TLB
中就会命中.
但是,我们要支持页可以从内存交换到磁盘,必须添加更多的机制,具体的说,当硬件在PTE
中查找时,可能发现页面不在物理内存中,硬件判断是否在内存中的方法,是通过页表项中的一条新信息,即存在位.
如果存在位设置为1,则表示该页存在于物理内存中,如果存在位设置为0,则表示该页不在物理内存中,而在硬盘中. 访问不在物理内存中的页,这种行为被称为页错误.
当发生页错误(页未命中),硬件产生中断,操作系统被唤醒处理页错误,执行页错误处理程序.
📖4. 页错误
如果一个页不在物理内存中,即它已经被交换到了硬盘上,在处理页错误时,操作系统需要将该页交换到内存中,那么有一个问题:操作系统怎么知道所需的页在哪里呢?
操作系统可以用PTE
中的某些位来存储硬盘地址,把存储PFN
的位置改为存储硬盘地址,当操作系统收到页错误时,它会在PTE
中查找地址,并将请求发送到硬盘,将页读取到内存中.
当硬盘I/O完成时,操作系统会更新页表,将此页标记为存在,更新页表项(PTE
)的PFN
字段以记录新获取页的内存位置,并重试指令. 下一次重新访问时TLB
还是未命中,然而这次因为页在内存中,因此会将页表中的地址更新到TLB
中(也可以在处理页未命中时顺便更新TLB
),最终的重试操作会在TLB
中找到转换映射,从已转换的内存物理地址,获取所需的数据或指令.
📖5. 为了处理页错误,操作系统大致做了什么?
首先,操作系统必须为将要换入的一个页找到一个物理帧,如果没有这样的物理帧,我们将不得不等待页面置换算法执行,并从内存中踢出一些页,释放帧供这里使用. 在获得物理帧后,处理程序发出I/O请求从交换空间中读取页,最后,当这个慢操作完成时,操作系统更新页表并重试指令,重试将导致TLB
未命中,然后硬件(MMU
)通过查找页表找到该项并缓存在TLB
中,然后再一次重试,此时硬件将能够访问所需的值.
📖6. 页面换出
刚才我们假设有足够的空闲内存来存储从交换空间换入的页. 但是,如果内存已经满了呢?
这时操作系统必须先交换出一个或多个页,以便为操作系统即将交换入的新页留出空间,选择哪些页被换出的策略,被称为页面置换.
📖7. 当程序从内存中读取数据会发生什么?
首先硬件从虚拟地址中拿出VPN
,并在TLB
中检查是否命中,当TLB
未命中时则需要查找页表中的PTE
,会分三种情况:
- 该页存在且有效,在这种情况下,
TLB
未命中处理程序可以从PTE
中获取PFN
,并将这对转换保存在TLB
中,接着重新执行指令,这次TLB
会命中,获得物理地址并在内存中读取到数据 - 页未命中处理程序需要运行,虽然它是一个有效页,但它此时不在物理内存中
- 访问的是一个无效页,可能由于程序中出现了错误,硬件捕获这个非法访问,操作系统陷阱处理程序运行,可能会杀死非法进程.
📖8. 页面置换什么时候真正发生
为了保证有少量的空闲内存,大多数操作系统会设置高水位线和低水位线(LW
),来帮助决定何时从内存中清楚页.
大致原理是这样:当操作系统发现有少于LW
个页可用时,后台负责释放内存的线程会开始运行,直到有HW
个可用的物理页,这个后台线程有时称为交换守护进程或页守护进程.
在执行交换的过程时,我们可以进行一些优化,例如,许多系统会把多个要写入的页聚集或分组,同时写入到交换空间中,从而提高硬盘的效率.
为了配合后台的分页线程,页面置换算法首先检查是否有空闲页,而不是直接执行替换,如果没有空闲页,会通知后台的分页线程按需要释放页(把它换出到磁盘上),当线程释放一定数目的页时,它会重新唤醒原来的线程,然后就可以把需要的页换进内存,继续执行.