Update 计算机操作系统 - 内存管理.md

把页面置换算法的引用块改为正文,补充分段在 x86-64 下的说明、页面置换的模拟结果和参考资料。
This commit is contained in:
CyC2018
2026-10-28 03:19:07 +08:00
parent 80f3696960
commit 118b134892
+36 -27
View File
@@ -13,6 +13,8 @@
* [分段](#分段)
* [段页式](#段页式)
* [分页与分段的比较](#分页与分段的比较)
* [附录:实测](#附录实测)
* [参考资料](#参考资料)
<!-- GFM-TOC -->
@@ -50,9 +52,7 @@
### 1. 最佳
> OPT, Optimal replacement algorithm
所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。
最佳置换算法(Optimal replacement algorithm, OPT)所选择的被换出的页面将是最长时间内不再被访问,可以保证获得最低的缺页率。
是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。
@@ -62,13 +62,11 @@
7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1
```
开始运行时,先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。
开始运行时,先将 7、0、1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。按这个规则走完整个序列共缺页 9 次;同样的序列和 3 个物理块,LRU 缺页 12 次,FIFO 缺页 15 次(逐步模拟,见[附录:实测](#附录实测))。
### 2. 最近最久未使用
> LRU, Least Recently Used
虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。
最近最久未使用(Least Recently Used, LRU):虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。
为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。
@@ -82,14 +80,12 @@
### 3. 最近未使用
> NRU, Not Recently Used
最近未使用(Not Recently Used, NRU)算法中,每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类:
每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类:
- R=0,M=0
- R=0,M=1
- R=1,M=0
- R=1,M=1
- 第 0 类:R=0,M=0;
- 第 1 类:R=0,M=1;
- 第 2 类:R=1,M=0;
- 第 3 类:R=1,M=1。
当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。
@@ -97,9 +93,7 @@ NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁
### 4. 先进先出
> FIFO, First In First Out
选择换出的页面是最先进入的页面。
先进先出(First In First Out, FIFO)算法选择换出的页面是最先进入的页面。
该算法会将那些经常被访问的页面换出,导致缺页率升高。
@@ -107,15 +101,13 @@ NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁
FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问题,对该算法做一个简单的修改:
当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。
当页面被访问(读或写)时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。
<div align="center"> <img src="pics/second-chance.png" width="720px"/> </div><br>
### 6. 时钟
> Clock
第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。
第二次机会算法需要在链表中移动页面,降低了效率。时钟(Clock)算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。缺页时检查指针指向的页面:R 位为 0 就换出它;R 位为 1 就清 0,指针前进一格继续检查。效果与第二次机会算法相同,但不需要移动页面。
<div align="center"> <img src="pics/clock.png" width="720px"/> </div><br>
@@ -135,10 +127,27 @@ FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问
## 分页与分段的比较
- 对程序员的透明性:分页透明,但是分段需要程序员显式划分每个段。
- 地址空间的维度:分页是一维地址空间,分段是二维的。
- 大小是否可以改变:页的大小不可变,段的大小可以动态改变。
- 对程序员的透明性:分页透明,但是分段需要程序员显式划分每个段;
- 地址空间的维度:分页是一维地址空间,分段是二维的;
- 大小是否可以改变:页的大小不可变,段的大小可以动态改变;
- 出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。
需要注意的是,x86-64 的 64 位模式下分段基本不再使用,代码段、数据段的基址都按 0 处理(FS、GS 除外,用于线程局部存储),Linux 等系统只用分页来管理内存。
## 附录:实测
用 Python 逐步模拟 3 个物理块下的页面置换,引用序列为 OPT 一节中的例子。Clock 的结果与新装入页面的 R 位取 0 还是 1、指针从哪里开始有关,这里装入时置 1:
```text
引用序列 [7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1] 物理块 3
OPT 缺页 9 次
LRU 缺页 12 次
FIFO 缺页 15 次
Clock 缺页 14 次
```
## 参考资料
- Tanenbaum A S, Bos H. Modern Operating Systems [M]. Pearson, 2014.
- Silberschatz A, Galvin P B, Gagne G. Operating System Concepts [M]. Wiley, 2018.
- [Page Tables — The Linux Kernel documentation](https://docs.kernel.org/mm/page_tables.html)