diff --git a/notes/pics/clock.png b/notes/pics/clock.png new file mode 100644 index 00000000..ea355824 Binary files /dev/null and b/notes/pics/clock.png differ diff --git a/notes/pics/lru-stack.png b/notes/pics/lru-stack.png new file mode 100644 index 00000000..b6c7c64c Binary files /dev/null and b/notes/pics/lru-stack.png differ diff --git a/notes/pics/page-translate.png b/notes/pics/page-translate.png new file mode 100644 index 00000000..0422bb50 Binary files /dev/null and b/notes/pics/page-translate.png differ diff --git a/notes/pics/second-chance.png b/notes/pics/second-chance.png new file mode 100644 index 00000000..f3ee602f Binary files /dev/null and b/notes/pics/second-chance.png differ diff --git a/notes/pics/segments.png b/notes/pics/segments.png new file mode 100644 index 00000000..86ceb1f3 Binary files /dev/null and b/notes/pics/segments.png differ diff --git a/notes/pics/vm-mapping.png b/notes/pics/vm-mapping.png new file mode 100644 index 00000000..eb004621 Binary files /dev/null and b/notes/pics/vm-mapping.png differ diff --git a/notes/计算机操作系统 - 内存管理.md b/notes/计算机操作系统 - 内存管理.md index 61eb5068..b00a6113 100644 --- a/notes/计算机操作系统 - 内存管理.md +++ b/notes/计算机操作系统 - 内存管理.md @@ -18,13 +18,13 @@ ## 虚拟内存 -虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。 +虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。此外,每个进程都有独立的地址空间,一个进程无法直接访问另一个进程的内存,操作系统还可以按页设置读写和执行权限。 -为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。 +为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,硬件触发缺页中断,由操作系统将缺失的页装入物理内存,再重新执行失败的指令。 从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0\~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。 -

+

## 分页系统地址映射 @@ -32,9 +32,13 @@ 一个虚拟地址分成两个部分,一部分存储页面号,一部分存储偏移量。 -下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位。例如对于虚拟地址(0010 000000000100),前 4 位是存储页面号 2,读取表项内容为(110 1),页表项最后一位表示是否存在于内存中,1 表示存在。后 12 位存储偏移量。这个页对应的页框的地址为 (110 000000000100)。 +下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位。例如对于虚拟地址 0x3ABC(0011 101010111100),前 4 位是页面号 3,读取表项内容为(101 1),页表项最后一位表示是否存在于内存中,1 表示存在。后 12 位是偏移量,直接拼接到页框号 101 后面,得到物理地址 0x5ABC(101 101010111100)。 -

+

+ +页表存放在内存中,每次访问内存之前都要先查一次页表。CPU 中的快表(Translation Lookaside Buffer, TLB)缓存最近用过的页表项,命中时不用再访问页表。 + +64 位系统的地址空间很大,如果用一张页表覆盖全部地址,页表本身就会非常大,因此使用多级页表,只为用到的地址分配下一级页表。例如 x86-64 通常使用 4 级页表,48 位虚拟地址分为 4 个 9 位的页表索引和 12 位的页内偏移。 ## 页面置换算法 @@ -54,7 +58,7 @@ 举例:一个系统为某进程分配了三个物理块,并有如下页面引用序列: -```html +```text 7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1 ``` @@ -70,11 +74,11 @@ 因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。 -```html +```text 4,7,0,7,1,0,1,2,1,2,6 ``` -

+

### 3. 最近未使用 @@ -105,7 +109,7 @@ FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问 当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。 -

+

### 6. 时钟 @@ -113,19 +117,17 @@ FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问 第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。 -

+

## 分段 虚拟内存采用的是分页技术,也就是将地址空间划分成固定大小的页,每一页再与内存进行映射。 -下图为一个编译器在编译过程中建立的多个表,有 4 个表是动态增长的,如果使用分页系统的一维地址空间,动态增长的特点会导致覆盖问题的出现。 +例如一个程序有代码、全局数据、堆和栈几部分,其中堆和栈会动态增长。如果放在一维的地址空间中,就要预先给每一部分留出位置,某一部分增长到相邻部分的位置时就会发生覆盖。 -

+分段的做法是把每一部分作为一个段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。地址由段号和段内偏移组成,通过段表找到段的基址,再加上偏移得到物理地址。 -分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。 - -

+

## 段页式 diff --git a/tools/diagram/src/clock.svg b/tools/diagram/src/clock.svg new file mode 100644 index 00000000..fbbe6153 --- /dev/null +++ b/tools/diagram/src/clock.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + 时钟算法:用环形链表代替移动页面 + + A R=1 + + B R=0 + + C R=1 + + D R=1 + + E R=0 + + F R=1 + + G R=0 + + H R=1 + + + 指针 + 发生缺页时,从指针处开始检查: + C 的 R = 1:清零,指针前进 + D 的 R = 1:清零,指针前进 + E 的 R = 0:换出 E,新页面放在这里, + 指针指向 F + 效果与第二次机会相同,但只移动指针, + 不用在链表中移动页面。 + 图中灰色的 C、D 在这次检查中被清零;淡紫色的 E 被换出。 + diff --git a/tools/diagram/src/lru-stack.svg b/tools/diagram/src/lru-stack.svg new file mode 100644 index 00000000..8989597d --- /dev/null +++ b/tools/diagram/src/lru-stack.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + LRU:5 个页框,链表头是最近访问的页面 + 访问 + 缺页 + 4 + + 4 + + + + + 缺页 + 7 + + 7 + + 4 + + + + 缺页 + 0 + + 0 + + 7 + + 4 + + + 缺页 + 7 + + 7 + + 0 + + 4 + + + 1 + + 1 + + 7 + + 0 + + 4 + + 缺页 + 0 + + 0 + + 1 + + 7 + + 4 + + 1 + + 1 + + 0 + + 7 + + 4 + + 2 + + 2 + + 1 + + 0 + + 7 + + 4 + 缺页 + 1 + + 1 + + 2 + + 0 + + 7 + + 4 + 2 + + 2 + + 1 + + 0 + + 7 + + 4 + 6 + + 6 + + 2 + + 1 + + 0 + + 7 + 缺页 + 换出 4 + 头 + 尾 + 被访问的页面移到链表头(淡紫色);访问 6 时 5 个页框已满,换出链表尾部最久没有访问的 4。11 次访问共缺页 6 次。 + diff --git a/tools/diagram/src/page-translate.svg b/tools/diagram/src/page-translate.svg new file mode 100644 index 00000000..13520ac5 --- /dev/null +++ b/tools/diagram/src/page-translate.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + 地址转换:虚拟地址 0x3ABC + 虚拟地址 + + 0 + + 0 + + 1 + + 1 + + 1 + + 0 + + 1 + + 0 + + 1 + + 0 + + 1 + + 1 + + 1 + + 1 + + 0 + + 0 + 页号 3 + 偏移量 0xABC(12 位) + 页表 + + 页框号 + + 存在 + 0 + + 010 + + 1 + 1 + + 111 + + 1 + 2 + + --- + + 0 + 3 + + 101 + + 1 + 4 + + --- + + 0 + 5 + + 000 + + 1 + ……共 16 项 + + 用页号作下标 + 物理地址 + + 1 + + 0 + + 1 + + 1 + + 0 + + 1 + + 0 + + 1 + + 0 + + 1 + + 1 + + 1 + + 1 + + 0 + + 0 + + 页框号 101(5) + + 偏移量原样复制 + = 0x5ABC + 4 KB 的页需要 12 位偏移量,16 位地址剩下的 4 位就是页号,页表一共 16 项。 + 如果查到的“存在”位是 0(例如页 4),说明这一页不在内存中,CPU 触发缺页中断。 + diff --git a/tools/diagram/src/second-chance.svg b/tools/diagram/src/second-chance.svg new file mode 100644 index 00000000..f41d3c91 --- /dev/null +++ b/tools/diagram/src/second-chance.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + 第二次机会:访问过的页面再给一次机会 + (a) 时间 20 缺页 + + A R=1 + 装入 0 + + + B R=0 + 装入 3 + + + C R=1 + 装入 7 + + + D R=0 + 装入 8 + + + E R=1 + 装入 12 + 最老的 A 的 R = 1:清零,移到队尾,装入时间改为 20;再看 B,R = 0,换出 B + (b) 处理之后 + + C R=1 + 装入 7 + + + D R=0 + 装入 8 + + + E R=1 + 装入 12 + + + A R=0 + 装入 20 + + + 新页 R=1 + 装入 20 + 队头是最早装入的页面。FIFO 会直接换出刚被访问过的 A,第二次机会算法换出的是又老又没被访问的 B。 + diff --git a/tools/diagram/src/segments.svg b/tools/diagram/src/segments.svg new file mode 100644 index 00000000..9b3ccd96 --- /dev/null +++ b/tools/diagram/src/segments.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + 分段:每个段是独立的地址空间 + + 代码 + 段 0 + 长 12K + + 全局数据 + 段 1 + 长 4K + + 堆 + 段 2 + 长 8K + + 可增长 + + 栈 + 段 3 + 长 8K + + 可增长 + 段表 + + 段号 + + 基址 + + 长度 + + 0 + + 0x4000 + + 12K + + 1 + + 0x9000 + + 4K + + 2 + + 0xC000 + + 8K + + 3 + + 0x1C000 + + 8K + 地址 = (段号 2, 偏移 0x300) + 0x300 < 8K,没有越界 + 物理地址 = 0xC000 + 0x300 = 0xC300 + 偏移 ≥ 段长时触发越界异常 + 各段长度不同、可以单独增长,并且可以按段设置权限(例如代码段只读);但段的大小不固定, + 内存中会留下大小不一的空闲碎片。段页式在每个段内再分页,兼顾两者。 + diff --git a/tools/diagram/src/vm-mapping.svg b/tools/diagram/src/vm-mapping.svg new file mode 100644 index 00000000..0987ac22 --- /dev/null +++ b/tools/diagram/src/vm-mapping.svg @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + 虚拟内存:64 KB 的地址空间,只有 32 KB 物理内存 + 虚拟页(4 KB 一页) + + 2 + 0K~4K + 0 + + 7 + 4K~8K + 1 + + × + 8K~12K + 2 + + 5 + 12K~16K + 3 + + × + 16K~20K + 4 + + 0 + 20K~24K + 5 + + × + 24K~28K + 6 + + × + 28K~32K + 7 + + 3 + 32K~36K + 8 + + × + 36K~40K + 9 + + × + 40K~44K + 10 + + 6 + 44K~48K + 11 + + × + 48K~52K + 12 + + × + 52K~56K + 13 + + 4 + 56K~60K + 14 + + × + 60K~64K + 15 + 物理页框 + + 页 5 + 0K~4K + 0 + + 空闲 + 4K~8K + 1 + + 页 0 + 8K~12K + 2 + + 页 8 + 12K~16K + 3 + + 页 14 + 16K~20K + 4 + + 页 3 + 20K~24K + 5 + + 页 11 + 24K~28K + 6 + + 页 1 + 28K~32K + 7 + + + + + + + + 左边格子中的数字是页框号,× 表示这一页不在内存中,访问它会触发缺页中断,由操作系统从磁盘调入。 + 只有 7 页在内存中,程序照样可以运行;各页映射到哪个页框没有顺序要求。 +