容量怎么算
最大容量看地址位数;实际容量取两者较小值:min(内存 + 外存, 2地址位数)
例:32 位地址(232 B = 4 GB),内存 512 MB,外存可用 2 GB。
- 最大容量 = 4 GB(地址只能编这么多)
- 实际容量 = min(0.5 + 2, 4) = 2.5 GB
如果外存换成 8 GB,实际容量就是 min(8.5, 4) = 4 GB:外存再大,地址也编不出来。
上半章的所有方案都要求进程整个装进内存才能运行。这一半把这个要求拿掉:只装当前要用的页,访问到不在内存的页再调进来,内存满了就挑一页换出去。难点集中在两处:缺页时到底发生了什么,以及挑哪一页换出去。
| 特征 | 含义 | 针对传统方式的哪个毛病 |
|---|---|---|
| 多次性 | 作业不用一次全部装入,允许分多次调入内存(最重要的特征) | 一次性 |
| 对换性 | 作业运行时不必一直常驻内存,允许换入、换出 | 驻留性 |
| 虚拟性 | 逻辑上扩充了内存容量,用户看到的内存远大于实际内存 | 前两个特征带来的结果 |
最大容量看地址位数;实际容量取两者较小值:min(内存 + 外存, 2地址位数)
例:32 位地址(232 B = 4 GB),内存 512 MB,外存可用 2 GB。
如果外存换成 8 GB,实际容量就是 min(8.5, 4) = 4 GB:外存再大,地址也编不出来。
连续分配要求进程在内存里占一整段。只装一部分、按需调入,意味着每次调进来的页落在哪个空闲块都不确定,必须靠页表逐页记录位置。
所以虚拟内存的三种实现方式都是离散的:请求分页、请求分段、请求段页式。
“虚拟性”靠多次性和对换性实现,而这两者又都建立在离散分配上。
记字段时就问一句:谁在什么时候读它。P 给地址变换机构,A 给置换算法,M 给置换时的写回判断,外存地址给缺页处理程序。
| 字段 | 记录什么 | 给谁用 | 什么时候改 |
|---|---|---|---|
| 页号 | 页表下标,实际不占存储(和上半章一样) | 定位表项:页表起址 + 页号 × 表项长度 | — |
| 物理块号 | 该页在内存中的块号,P = 1 时才有效 | 地址变换:块号 ‖ 页内偏移 | 调入时填写 |
| 状态位 P(存在位) | 该页是否已调入内存 | 地址变换机构:P = 0 就发缺页中断 | 调入置 1,换出置 0 |
| 访问字段 A | 最近是否被访问过 / 被访问次数 / 多久没被访问 | 置换算法选淘汰页(LRU、CLOCK) | 访问时由硬件置位或更新;CLOCK 扫描时清 0 |
| 修改位 M(脏位) | 调入内存后是否被写过 | 置换时决定要不要写回外存 | 写操作时由硬件置 1 |
| 外存地址 | 该页在外存上的位置,通常是外存块号 | 缺页处理程序调入该页时按它去读 | 换出到对换区时可能改变 |
绿色是快表命中的捷径,红色是缺页路径。缺页处理结束后,不是接着往下走,而是回到开头把这条指令重新执行一遍。
内中断(异常)里的故障(fault)。它由当前指令访问了不在内存的页引起,和外设无关;而且是可以修复的错误,修好了这条指令就能正常执行。
OPT 看未来,LRU 看过去的使用时间,FIFO 只看进入内存的先后,CLOCK 用一个访问位粗略近似 LRU。先想清楚依据,特点和代价都能推出来。
淘汰以后永远不用、或者最长时间内不再被访问的页
特点缺页率最低,是所有算法的上限
代价无法实现:OS 不可能预知进程以后的访问序列。只用来当标准,评价其他算法
淘汰最早进入内存的页(队列头)
特点实现最简单,一个队列就够
代价进来得早不代表用得少,常用的页(如主循环所在页)也会被换出,性能差;会出现 Belady 异常:分配的物理块增加,缺页次数反而增加
淘汰过去最长时间没被访问的页
特点用“过去”估计“未来”(时间局部性),性能最接近 OPT;属于栈式算法,不会出现 Belady 异常
代价每次访问都要更新使用时间,需要寄存器和栈等硬件支持,开销大
淘汰访问位 A = 0 的页
做法内存中的页连成循环队列,指针依次扫描:A = 1 就清 0、跳过(给第二次机会),A = 0 就换出。新页装入时 A = 1,指针指向下一块。
轮数第一轮可能把所有 A 都清成 0,第二轮一定找到,最多两轮
同样是最近没用过,换出没改过的页不用写回磁盘,更省。按 (A, M) 把页分成四类:
| 类 | (A, M) | 含义 | 淘汰优先级 |
|---|---|---|---|
| 第 1 类 | (0, 0) | 最近没访问,也没修改 | 最先淘汰,换出不用写回 |
| 第 2 类 | (0, 1) | 最近没访问,但修改过 | 其次,换出要写回 |
| 第 3 类 | (1, 0) | 最近访问过,没修改 | 可能很快再用 |
| 第 4 类 | (1, 1) | 最近访问过,也修改过 | 最后才淘汰 |
默认是教材上最经典的一串,分 3 个物理块。切换算法,单步往前看每次淘汰的理由;也可以改访问串和块数。
约定:物理块初始为空;前几次访问把页装进空闲块,也计缺页,但不算置换。所以 置换次数 = 缺页次数 − 被填满前的装入次数。
驻留集:请求分页中,分配给一个进程的物理块的集合。驻留集太小,缺页频繁;太大,能同时装入的进程变少,并发度下降。
| 策略 | 驻留集大小 | 缺页且没空闲块时 | 问题 |
|---|---|---|---|
| 固定分配 局部置换 | 运行前分好,运行期间不变 | 只能换出自己的页 | 很难事先确定该给多少:给少了频繁缺页,给多了浪费 |
| 可变分配 全局置换 | 可变:缺页时从 OS 的空闲块队列再分一块 | 空闲块用完后,可以换出系统中任一进程的页(未锁定的) | 被换出页的进程块数变少,它的缺页率上升;进程之间互相影响 |
| 可变分配 局部置换 | 可变:根据缺页率动态调整 | 只换出自己的页;缺页频繁就多给几块,缺页率很低就收回一些 | 需要持续统计缺页率,实现较复杂,但效果最好 |
外存分两块:对换区连续分配,读写快;文件区离散分配,读写慢。
现象刚换出的页马上又要访问,只好再换入;刚换入的页又被换出。进程大部分时间花在换页上,CPU 利用率骤降。
根本原因分配给进程的物理块不够:它频繁访问的页面数高于可用的物理块数。置换算法选得不好也会加剧。
怎么办给进程更多块(让驻留集 ≥ 工作集);块不够时降低多道程序度,挂起(换出)部分进程,把腾出的块分给其余进程。
定义在某段时间间隔(窗口 Δ)里,进程实际访问过的页面集合,记作 W(t, Δ)。
大小工作集大小 ≤ Δ,局部性越好,工作集越小(重复访问同几页)。
分配的物理块数(驻留集)不能小于工作集大小,否则就会频繁缺页,走向抖动。也可以用它选淘汰页:优先换出不在工作集里的页。
| 时刻 t | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 工作集 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 访问页 | 1 | 2 | 3 | 2 | 1 | 4 | 4 | 4 | 5 | 4 | |
| W(5, 4) | 2 | 3 | 2 | 1 | {1, 2, 3},大小 3 | ||||||
| W(10, 4) | 4 | 4 | 5 | 4 | {4, 5},大小 2 |
往回数 Δ 次访问(含当前这次),去重就是工作集。t = 10 时进程集中在页 4 附近,工作集只有 2 页,这时给它 2 块就够;t = 5 时至少要 3 块。
OS 把一个磁盘文件映射到进程虚拟地址空间的一段,进程之后像访问内存一样读写文件,不用再调 read / write。
| 对比项 | Cache ↔ 主存(计组) | 主存 ↔ 辅存(虚拟内存) | 快表 TLB |
|---|---|---|---|
| 目的 | 解决主存速度不够 | 解决主存容量不够 | 加快地址变换 |
| 缓存的内容 | 主存块(几十字节) | 页(KB 级) | 页表项 |
| 由谁管理 | 全部由硬件完成 | 硬件(地址变换、缺页中断)+ OS(调页、置换) | 硬件 |
| 透明性 | 对所有程序员透明 | 对应用程序员透明,对系统程序员不透明 | 对程序员透明 |
| 映射方式 | 直接 / 全相联 / 组相联 | 全相联(任一页可放任一块,由页表记录) | 常用全相联或组相联 |
| 不命中的代价 | 访问主存,几十到上百 ns | 访问磁盘,ms 级,差 5~6 个数量级 | 多访存一次查页表 |
| 不命中时 CPU | 等待,硬件直接从主存调块 | 缺页中断,进程阻塞,CPU 转去运行别的进程 | 硬件或 OS 查页表后装入 |
| 写策略 | 全写法或写回法 | 只能写回(修改位 M,换出时才写) | — |
| 替换算法 | 硬件实现:随机、FIFO、LRU | 软件实现:OPT(理论)、FIFO、LRU、CLOCK | 硬件实现 |
一次访存只可能落进三种情况之一:快表命中、快表未命中但页在内存、缺页。每种情况的时间照着上面的流程图数一遍就出来了。
记快表访问时间 tT,内存访问时间 tm,快表命中率 α,缺页率 p(占全部访问的比例,缺页一定是快表未命中,所以 p ≤ 1 − α),缺页处理时间 Tf。
tT = 10 ns,tm = 100 ns,α = 0.9,p = 0.00001,Tf = 10 ms = 107 ns。
十万次访问里才缺一次页,却贡献了将近一半的时间。缺页处理(磁盘 I/O)太贵,所以缺页率必须压得非常低。
题目的约定会变:有的说快表和页表同时查(未命中时不再加 tT);有的说缺页处理后直接访存而不是重新查快表;有的不考虑快表。先按题意把每种情况的步骤数出来,再套加权。常考题型是给几个逻辑地址,分别问每个地址的访问时间,那就是分别落在 ①②③ 哪一种。
| 算法 | 淘汰依据 | 能否实现 | Belady 异常 | 开销 / 硬件 | 一句话 |
|---|---|---|---|---|---|
| OPT | 未来最久不用 | 不能 | 不会 | — | 缺页最少,当标准用 |
| FIFO | 进入内存最早 | 能 | 会 | 最小,一个队列 | 最简单,性能差 |
| LRU | 过去最久没用 | 能 | 不会 | 大,需要寄存器和栈 | 最接近 OPT |
| CLOCK / NRU | 访问位 A = 0 | 能 | 考试按“只有 FIFO”作答 | 小,一位访问位 + 指针 | 最多扫描 2 轮 |
| 改进型 CLOCK | (A, M) 优先 (0, 0) | 能 | 同上 | 小,访问位 + 修改位 | 最多扫描 4 轮,少写回 |
关于 CLOCK 与 Belady:CLOCK 在所有访问位都为 1 时退化为 FIFO,严格说也可能出现 Belady 异常(在模拟器里用 CLOCK 跑那串 1,2,3,4,1,2,5,… 就能看到);但王道和 408 选择题的结论是“只有 FIFO 会出现”,考场上照此作答。