由于所有用户进程总的虚拟地址空间比可用的物理内存大很多,因此只有最常用的部分才与物理页帧关联。这不是问题,因为大多数程序只占用实际可用内存的一小部分。
在将磁盘上的数据映射到进程的虚拟地址空间的时,内核必须提供数据结构,以建立虚拟地址空间的区域和相关数据所在位置之间的关联。例如,在映射文本文件时,映射的虚拟内存区必须关联到文件系统的硬盘上存储文件内容的区域。如图所示:
当然,给出的是简化的图,因为文件数据在硬盘上的存储通常并不是连续的,而是分布到若干小的区域。内核利用address_space数据结构,提供一组方法从后备存储器读取数据。例如,从文件系统读取。因此address_space形成了一个辅助层,将映射的数据表示为连续的线性区域,提供给内存管理子系统。
按需分配和填充页称为按需调页法。它基于处理器和内核之间的交互,使用的各种数据结构如图所示:
过程如下:
CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步(如下图):
首先,将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!!!),CPU要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址,
其次,再利用其页式内存管理单元,转换为最终物理地址。
这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽像给进程。之所以这样冗余,Intel完全是为了兼容而已。
- 进程试图访问用户地址空间中的一个内存地址,利用上面的线性地址去查找页表,确定对应的物理地址,但使用的页表无法确定物理地址(物理内存中没有关联页)
- 处理器接下来触发一个缺页异常,发送到内核。
- 内核会检查负责缺页区域的进程地址空间数据结构,找到适当的后备存储器,或者确认该访问实际上是不正确的(未映射,未使用)
- 分配物理内存页,并从后备存储器读取所需数据填充。
- 借助于页表将物理内存页并入到用户进程的地址空间,应用程序恢复执行。
这些操作对用户进程是透明的。换句话说,进程不会注意到页是实际在物理内存中,还是需要通过按需调页加载。
在整个过程中可能需要解决以下几个问题:
1)系统如何感知进程当前所需页面不在主存(页表机制);
2)当发现缺页时,如何把所缺页面调入主存(缺页中断机构);
3)在置换页面时,根据什么策略选择欲淘汰的页面(置换算法)。
页表机制
状态位(中断位):标识该页是否在内存(0或1);
访问位:标识该页面的近来的访问次数或时间(换出);
修改位:标识此页是否在内存中被修改过;
外存地址:记录该页面在外存上的地址,即(外存而非内存的)物理块号。
缺页中断机制
程序在执行时,首先检查页表,当状态位指示该页不在主存时,则引起一个缺页中断发生,其中断执行过程与一般中断相同:
保护现场(CPU环境);
中断处理(中断处理程序装入页面);
恢复现场,返回断点继续执行。
置换算法
FIFO
LRU
LFU