- 作者: 雪山肥鱼
- 时间:20210308 22:28
- 目的:Swap Page
# 1. 页的换入换出机制
## 1.1 Swap Space 交换空间
## 1.2 The Present Bit
## 1.3 Page Fault
## 1.4 何时发生swap out
# 2. 交换机制
## 2.1 Other VM policies
## 2.2 Trashing 概念 抖动
# 3.VAX/VMS 内存系统
## 3.1 内存管理硬件
## 3.2 VAX 页替换
## 3.3 影响后来者的 VM 内存管理技巧(demanding page & Cow)
1. 页的换入换出机制
放开最大的假设,物理内存可以装的下地址空间。实际上这是不可能的,所以OS将地址空间中不常用的内存,转移到硬盘中。
1.1 Swap Space - 交换空间
- Swap Space 是定义在disk 上的一块空间,pages 可以在这里换入换出。
- Swap Space 的单位是 page-united 的。
-
OS 需要记住这片空间的物理硬盘地址。
举例:
每个进程所用到的page,并不全在物理内存中,其中不进场用的存放在硬盘的交换空间中。
1.2 The Present Bit
我们需要增加将page 换入换出的机制,换出暂且不提,这里先考虑换入的机制
- 考虑page换入机制:也是取数据的流程
a. CPU 给出一个地址,优先经过MMU计算VPN(仅过了多级页目录的计算)
b. 去TLB中寻找,找到了,则与offset 进行组合,找到物理地址
c. 没有在TLB中找到,通过MMU 找到page table所在位置,拿着VPN 去 page table 进行查找。
d. 检查page table 中的 present bit 是否为1
e. 为1,找到对应的PTE,提取其中的PFN,结合offset 得到真正的物理地址
f. 为0,产生page fault,触发页交换机制 - 为什么叫page fault 而不叫 page miss
- fault的叫法有一定历史原因,也因为OS handle fault的一种机制
- 当一些不正常的硬件问题发生时,硬件会将控制权转移给OS,希望OS能正确处理。
- 比如,一个进程要访问的页并不在物理内存中,这种情况,硬件就只能报出一个异常,OS接管,所以这里叫做page fault,是从硬件角度出发的。
1.3 Page Fault
当page 不存在于物理内存,而被交换到了disk上,此时又有进程要访问这个page时,OS 需要将产生 swap page的操作去迎合 page fault 所产生的异常。
- 问题1,OS 去硬盘的哪个位置寻找想要的page呢?
在很多系统中,页表中的Page Table Entry, 页表项会存储对应的disk address. 当OS 收到page fault时,可以利用对应的PTE 去查找address,从而向disk 发出I/O 请求。 - 问题2:硬件为什么不处理page fault
a. 硬件 配合disk去处理 page fault 会非常的慢
b. 能够处理page fault,硬件需要懂得swap space 的概念,并且知道如何向disk发出I/O 请求等一些列细节操作,会非常复杂。
当disk I/O请求结束,OS会更心page table, 将PTE 对应的 present 位设置位1,并更新 PFN,(已经更新到physical address 中了)。
其实也会出发TLB Miss,毕竟刚刚被swap in 到了 内存中。
对disk 产生I/O 申请 去查询page 所在disk中的位置时,进程时阻塞的,那么OS此时会去运行其他的进程,直到page fault 被解决。等待I/O的过程是昂贵的,索性去做其他事情
VPN = (VirtualAddress & VPN_MASK) >> SHIFT
(Sucess, TlbEntry) = TLB_Lookup(VPN)
if(Success == True)
if(CanAccess(TlbEntry.ProtectionBits) == Ture)
Offset = VirtualAddress & Offset_MASK
PhyAddr = (TlbEntry.PFN<
Page falut Control Flow Algorithm(Software)
PFN = FindFreePhysicalPage() //先看内存里有没有地方swap in
If(PFN == -1) //没有将某些page踢出去
PFN = EvictPage()
DiskRead(PTE.DiskAddr, PFN) //在disk上寻找,并刷新PFN
PTE.present = True
PTE.PFN = PFN
RetryInstruction() // 重新执行指令
1.4 何时发生 swap out
当内存满了,再去进行 内存的交换,从而发生swap out 这是很不现实的。OS 必须保证一直有一部分内存是空闲的。
- high watermark 高水位线
- low watermark 低水位线
两者结合产声 evicting page.
其实主要用了page daemon 或者叫做 swap daemon 守护进程。
物理内存容量低于低水位线 -> evicting page
物理内存容量高于高水位线 -> 不做交换
现象:有的时候什么都不做,电脑磁盘会一直在转一直在响,那么就是在做paging的动作
2. 交换机制
page 的交换机制大部分都封装在OS中,程序员不用关心。
其实这一章节的主要内容就是对比各种交换机制的效率和优劣。
最终推荐使用的是 LRU,本质上用到了局部性原理
空间局部性:P 被访问了,那么P-1 P+1 都有可能被访问
时间局部性:最近被访问了,那么有很大概率会再次被访问
LRU: Least-Recently-Used:最近最少使用
LFU: Least-Frequently-Used:最低频率使用
LRU 也会借助硬件,记录一个 reference bit,结合reference bit 的 clock algorithm 可以实现LRU。
2.1 Other VM Policies
除了 page replacement,还会有其他策略,比如 page selection,中的 demand paging. 也就是说 OS 会做到 预测。比如说page P 被带到了内存中,被访问了,那么Page P+1 有可能被访问到,那么也会被带进内存。
OS如何将page 写进disk中:
page 的 clustering / group 写策略,抱团写,提高效率。
2.2 Trashing 概念 抖动
当活动的进程使用的内存超过了实际物理内存的大小,那么OS 会频繁的进行 paging 的操作,page的换入换出。比如大型游戏的多开,会出现硬盘一直在转的现象,paging 的导致的Trashing 现象。
这个时候OS的处理方式就是杀掉某些进程,减少进程工作集。linux的处理方式就是杀掉内存密集型的进程。
3 VAX/VMS 内存系统
让我们看下VAX/VMS 操作系统的虚拟内存管理
3.1 内存管理硬件
- VAX为每个进程提供了一个32位的虚拟地址空间
- page size 512个字节
- 23 bit VPN + 9 bit offset. 高2位用于分页所在的段
- 该系统是分页和分段的混合体
- P0 和 P1 组成了 Progress Process.
- S 是 system space。这一段是跨进程共享的。
VMS的问题很明显,就是page size 调小了,会导致现性的page table 很大,所以位P0 P1 每个区域分别提供了一个页表。所以 堆栈之间未使用的部分并没有分配任何内存。
base register 存储基地址 bounds register 存储界限寄存器,即页表的数量
- 为什么空指针访问会导致段错误
int *p = NULL;
*p = 10
硬件试图在TLB中查找VPN(0),遇到TLB未命中。查询页表,并且发现VPN 0 的条目别标记为无效,因此,我们遇到无效的访问,将控制权交给OS,这可能会中止进程(在UNIX系统上,会向进程发出一个信号,让他们对这样的错误做出反应,但是如果信号未被捕获,则会中止进程)
所以0页存在的原因 也是为了检测null-pointer 访问提供支持
内核虚拟地址空间(S)是每个用户地址空间的一部分。在上下文切换时,操作系统改变p0和p1寄存器以指向即将运行的进程的适当页表,但是不会更改S基址和界限寄存器,并因此将相同 内核结构映射到每个用户地址空间。
原因:
- 如果OS收到用户程序 write 递交的指针,很容易将数据从该指针处赋值到它自己的结构中。
- 如果内核完全位于物理你内存中,那么将页表的交换切换到磁盘时非常困难的。
- 如果内核被赋予了自己的Address Space,那么用户应用程序和内核之间移动数据会变得非常痛苦。
- 所以内核几乎就像应用程序库一样,尽管是收到保护的
OS当然不想让应用程序读取OS的数据和代码。从硬件角度进行支持,对page 进行分级。OS的data 和 code 的保护等级一定会高于应用程序的等级。当然如果应用程序准备访问高等级的OS代码,那么一定会产生trap,并且OS会杀掉你这个有冒犯的进程
3.2 VAX的页替换
VAX的页表项包含:
- 有效位
- 保护字段
- 脏位
- OS使用的保留字段
- PFN
- no reference bit
3.2.1 分段的FIFO
VAM提出了 分段FIFO 替换策略:
- 每个进程都有一个可以保存在内存中的最大页数,Resident Set Size , RSS 驻留集大小。
- 每个页都保存在FIFO列表中,当一个进程超过其RSS时,先入的页被驱逐。FIFO不需要硬件支持
纯粹的FIFO 性能并不友好。为了提高FIFO的性能,VMS引入了 二次机会列表。页在从内存中被踢出去之前被放在其中。具体说:
- 全局的干净页空闲列表
- 脏页列表
当进程P超过其RSS时,将从其每个进程的FIFO中移除一个页。如果未被修改,将其放在未被修改列表的末尾。如果脏了(以呗修改)将其放在脏页列表的末尾。
分段的FIFO算法接近LRU
3.2.2 页聚集
为了让交换I/O更有效,VMS增加优化,通过 聚集 clustering,将大批量的页从全局脏页列表中分组到一起,一举写入磁盘。
3.3 其他机智的VM管理技巧
3.3.1 按需置零 - 有需求时才会申请
以堆为例:通常情况将一页内存添加到address space 中。
- OS在物理内存找到页,,为了安全期间置零
- 映射到Address Space
- 这种实现有缺陷,特别是当申请了这页,但是没有用到!
利用按需置零方法,OS 在遇到内存申请请求时,其实做的事情并不多。
遇到内存申请时:
- 在页表中标记这一项 PTE 是不可访问的,但是设置了不可访问的原因是demand-zero
- 如果进程写或者读这一块内存,那么操作系统才会遇到trap
- OS查看原因,原来是 demand zero 在这个时候才会去向physcial memory 申请内存
其实意识就是 malloc 了1G 内存,不会立刻给你,用到的时候再给你。
3.3.2 COW copy on write 读时共享 写 时复制
- 使用背景: OS 将 A 的 地址空间中的一页,拷贝到B 的地址空间中
- 并不是真的copy, 而是将这一页映射到进程B, 同时会将这页设置成 cow(其实是read only)属性
- 如果两个进程都只是读这一页,那么OS没反应
- 当其中一个进程 尝试去写这个进程,OS会遇到trap,发现这一页是cow, 此时复制这一页,包括这一页的所有数据。
Unix 系统中的fork()和 exec() 的配合使用,就是利用COW技术。替换了address space 的 代码段。
开发VAX/VMS DEC公司对后续操作系统的影响很大,包括DEC在当时也是如日中天,但是由于决策的问题,也终究推出了历史舞台。