文章目录
7.1 请求调页
我们程序都是放在磁盘中,我们CPU执行程序的时候,程序需要加载到内存中,也就是我们如何从磁盘加载可执行程序到内存。
一种选择是:在程序执行时将整个程序加载到物理内存。(这个我们在前面讲过了,加载整个程序太浪费内存了,又不一定用的到)
另一种选择是:仅在需要时才加载页面。
不用想就知道肯定选最后一种,因为程序的局限性一般最近执行的都在这附近的几个页中,等到需要的时候才加载进内存,这种技术被称为请求调页,常常用于虚拟内存系统。
7.1.1 基本概念
当换入进程时,调页程序会猜测在该进程被再次换出之前会用到哪些页。调页程序不是调入整个进程,而是把那些要使用的页面调入内存。这样,调页程序就避免了读入那些不使用的页,也减少了交换时间和所需的物理内存空间。
使用这种方案需要硬件的支持,因为我们需要知道那些页已经调入到内存中,如果不在内存中,我们也需要知道那些页在磁盘中的位置。
这里王道考研介绍的页表还挺不错的,这里引用一下:
页号 | 内存块号 | 状态位 | 访问字段 | 修改位 | 外存地址 |
---|---|---|---|---|---|
0 | 无 | 0 | 0 | 0 | x |
1 | b | 1 | 10 | 0 | y |
2 | c | 1 | 6 | 1 | z |
页号是虚拟内存的页号;
内存块号是物理内存的块号;
状态位表示的是是否已经加载进内存;
访问字段这个是做为统计的,后面一节会介绍,如果是LRU就会根据这个字段就会把页面换出;
修改位是内存中有需要,需要写入磁盘;
外存地址是没换入内存中,存储在磁盘的地址。
(注意:理论上是这么实现的,具体的是怎么做的呢?我们需要在后面的源码篇去分析一下linux内核)
7.1.2 访问的地址在内存中
如果进程尝试去访问一个地址,这个地址刚好在页表中,那就按照之前的方式来访问。
通过页表来查找对应的物理内存块,然后根据偏移和物理内存块计算出具体物理地址。(如果是在TLB的时候就中了,那就更快了,反正都是获取到物理内存块)
7.1.3 访问的地址不在内存中,但是内存有空闲页
但是吧,如果进程视图访问尚未调入内存中的页面时,情况会如何??
对状态位为0(未加载进内存中)的页面访问会产生缺页错误。MMU在通过页表转换地址时会注意到状态位为0,从而陷入操作系统。此时缺页的进程进入阻塞状态,直到调页完成之后,才把该进程唤醒(其实是放入就绪状态)。
进程尝试去访问一个虚拟地址,接下来的操作:
- 首先访问TLB,明显TLB不存在这个页表的缓存。
- 然后去访问页表,以确定这个页是否已经加载到内存中。
- 如果没加载到内存中,进程阻塞,然后进入缺页中断。
- 后面就是缺页中断程序的实现
- 找到一个空闲页(如果没有空闲页的话,需要页面置换,这个后面介绍)
- 调度一个磁盘操作,以将所需的页面读到刚分配的帧(突然想到页表都没加载,怎么知道在磁盘那个位置???)
- 当磁盘读取完成后,修改进程的页表,表示该页已经在内存中了。
- 重新启动被陷阱中断的指令。(相当于再次走一遍流程,只不过这次是在TLB或者在页表中了)
这就是一个完成的过程,黄色线是请求页加载到内存,到重启指令的反过程,重启指令完成之后,又再次从CPU出发。
支持请求调页的硬件与分页和交换的硬件相同:
-
页表:该表能够通过有效-无效位或保护位的特定值将条目标记为无效
-
外存:外存通常是高速硬盘,称为交换设备,用户交换的这部分磁盘称为交换空间
7.1.4 访问的地址不在内存中,内存没有空闲页
这种情况在上面又讲过,需要页面置换算法,这个我们在后一篇详细介绍。
7.1.5 调页性能
这个调页请求很明显会影响到我们进程的执行,因为如果有缺页中断,需要读取磁盘,这一部分耗费很大,这个其实跟我们之前介绍的TLB命中率差不多,需要控制这个缺页中断的出现,这样子效率自然提高了。
7.2 总结
这一篇也是比较简单,就是进程执行的时候,如果在TLB和页表中都没有找到这个页面,需要去磁盘中读取,然后在修改页表或TLB表,从而恢复指令,CPU会再次执行这个流程,但是这次操作会直接在TLB表或在页表中找到了物理页,进行计算出具体的物理内存。
理论篇介绍到这里,其实已经需要源码篇来证明了,不过不要着急,理论篇快结束了。