OS操作系统系列文章目录
【OS操作系统】Operating System 第一章:操作系统的概述
【OS操作系统】Operating System 第二章:启动、中断、异常和系统调用
【OS操作系统】Operating System 第三章:连续内存分配
【OS操作系统】Operating System 第四章:非连续内容分配
【OS操作系统】Operating System 第五章:虚存技术
【OS操作系统】Operating System 第六章:页面置换算法
【OS操作系统】Operating System 第七章:进程与线程
【OS操作系统】Operating System 第八章:处理机调度
【OS操作系统】Operating System 第九章:同步互斥问题
【OS操作系统】Operating System 第十章:信号量与管程
【OS操作系统】Operating System 第十一章:死锁与进程通信
【OS操作系统】Operating System 第十二章:文件系统
目录
第四章:非连续内容分配
非连续内存分配的必要性
-
连续内存分配的缺点:
- 分配给一个程序的物理内存是连续的;
- 内存利用率低;
- 有外碎片、内碎片的问题;
-
非连续内存分配的优点:
- 一个程序的物理地址空间是非连续的;
- 更好地利用和管理内存;
- 允许共享代码与数据(共享库等);
- 支持动态加载和动态链接;
-
非连续内存分配的缺点:
- 软件方案(开销太大);
- 硬件方案(开销较小):
- 分段;
- 分页;
非连续内存的分配
分段(Segmentation)
- 逻辑地址空间是连续的,物理地址空间是离散的,使用映射机制进行关联;
-
逻辑地址由有序对组成,表现为二维地址,
<段号s,偏移d>
; -
其中,段号指的是物理地址空间,一个内存块;
-
段表:由OS进行维护;
- 里面每一个条目都有段基地址和段界限;
- 段基地址:包含该段在内存中的起始物理地址;
- 段界限:指定了该段的长度;
- 分段的寻址方案
- 程序访问,CPU给出逻辑地址;
- 根据逻辑地址的段号,在OS维护的段表中,找到对应的起始物理地址;
- 并比较段表给出的段界限和逻辑地址的偏移量;
- 若偏移合法,这得到物理地址,即段表中的起始地址 + 二元组中的偏移地址;
分页(Paging)
- 地址空间
- 帧:将物理内存空间划分为固定大小的帧;
- 页:将逻辑地址空间划分为相同大小的页;
- 帧页的大小为2的幂次方;
- 建立方案:转换逻辑地址为物理地址(pages to frams)
- 页表(page table);
- MMU、TLB(快表);
-
物理地址空间:帧
分页和分段的最大区别:帧的大小是固定的,段的大小是不定的;
内存的物理地址表示为一个二元组
(f, o)
,即(帧号, 帧内偏移量);- 帧号:共有F位二进制,表示物理地址空间有 2 F 2^F 2F个帧;
- 帧内偏移量:共有S位二进制,表示每帧的大小为 2 S 2^S 2S个字节;
- 物理地址: 2 S ∗ f + o 2^S * f + o 2S∗f+o;
-
实例:
-
逻辑地址空间:页
页号的作用就是通过二元组得到一个逻辑地址;
而页号与帧号不是相等的;
但是它们的偏移量是相等;内存的逻辑地址页表示为一个二元组
(p, o)
,即(页号, 页内偏移量);- 页号:共有P位二进制,表示逻辑地址空间有 2 P 2^P 2P个页;
- 页内偏移量:共有S位二进制,表示每页的大小为 2 S 2^S 2S个字节;
- 虚拟地址: 2 S ∗ p + o 2^S * p + o 2S∗p+o;
-
分页寻址方案:
OS维护了一张页表,页表保存了逻辑地址和物理地址之间的映射关系;
页表实际上可以认为是一个哈希表,index
是页号,value
是帧号;- 寻址过程:
- 根据逻辑地址计算得到一个页号;
- 根据页号,在页表中寻找到对应的帧号;
- 根据帧号和偏移量,计算得到物理地址;
- 特点:
- 逻辑地址空间应当大于物理地址空间;
- 页是连续的虚拟内存;
- 帧是非连续的物理内存(有助于减少碎片的产生);
- 不是所有的页都有对应的帧;
- 寻址过程:
页表和快表
-
页表概述:
每个运行的程序都有一个页表
- 属于程序运行状态,会动态变化;
- PTBR:页表基址寄存器;
- 页表的每个表项都存储了特殊标志位:
- dirty bit;
- resident bit(0:对应的帧在内存中不存在;1:存在);
- clock/reference bit;
- 实例:
- 逻辑地址(4,0),页号4对应的二进制是100,它的位置对应着flags;
- 根据上图,可知它的dirty bit是1,resident bit是0,clock/reference是0;
- 因此可以知道逻辑地址(4,0)在物理地址中实际是不存在的。如果CPU访问这个逻辑地址会抛出一个内存访问异常;
- 逻辑地址(3,1034),页号3对应的二进制是011,它的位置(也就是页号的位置)对应着flags;
- 根据上图,可知它的dirty bit是0,resident bit是1,clock/reference是1;
- 因此可以知道逻辑地址(3,1034)在物理地址中存在;
- 根据页表,页号3对应的页帧号是4,再加上它们的偏移量相等,所以逻辑地址(3,1034)对应的物理地址是(4,1023);
- 逻辑地址(4,0),页号4对应的二进制是100,它的位置对应着flags;
- 分页机制的性能问题
- 访问一个内存单元需要2次内存访问:
- 一次用于获取页表项;
- 一次用于访问数据;
- 页表可能会非常大(约为 2 页 号 位 数 2^{页号位数} 2页号位数);
- 每一个运行的程序都需要一个页表;
- 访问一个内存单元需要2次内存访问:
- 解决办法:
- 缓存(caching);
- 间接访问(indirection);
转换后备缓冲区(快表TLB)
使用缓存的方法,优化分页表的时间开销问题;
Translation Look-aside Buffer
(TLB)是CPU的MMU保存的一段缓存,这段缓存保存的内容 是页表的一部分,其内容是那些需要经常访问到的部分页表,其余不常用的页表内容仍保存在内存中;
- 缓存近期访问的页帧转换表项:
- TLB使用关联内存实现,具备快速访问性能;
- 如果TLB命中,物理页可以很快被获取;
- 如果TLB未命中,对应的表现被更新到TLB中(x86的CPU由硬件实现,其它的可能是由操作系统实现的);
二级页表和多级页表
- 通过多级页表解决优化页表造成的空间开销问题:
- 虽然增加了内存访问次数和开销,但是节省了保存页表的空间;
- 相当于通过时间代价换取空间的优化,同时通过快表TLB也可以减少时间的消耗;
二级页表
- 在逻辑地址中,页号被分为两个部分,分别是
p1
和p2
,其中一级页号对应着一级页表,二级页号对应着二级页表(二级页表不止一个); - 通过
p1
可以在一级页表中获取到对应二级页表的起始地址,该起始地址加上二级页号的地址值,可以在二级页表中获取到物理地址的帧号,并最后在偏移量的计算下,获得实际的物理地址;
- 这个方法节约了一定的空间:
- 在一级页表中,如果
resident bit = 0
,可以使得在二级页表中不再存储相关的index
; - 如果只有一张页表的话,这些没必要的
index
就要保留下来;
- 在一级页表中,如果
多级页表
- 通过将页号分为
k
个部分,实现多级的间接页表,相当于建立了一棵页表树; - 例如64位系统,采用了5级页表;
反向页表
- 传统页表的缺点:
- 对于大地址空间,前向映射页表变得繁琐,如64位系统采用5级页表;
- 逻辑地址空间的增长速度远快于物理地址空间,所有采用反向页表,用
index
表示物理地址,value
表示逻辑地址,则该页表将会远小于传统页表的大小;
- 反向页表:
- 为了解决大地址空间的问题,希望通过物理地址的帧号获取逻辑地址的页号,而建立反向页表;
- 反向页表建立后,只需要存在一张即可;
- 存在的问题:映射关系的建立比较困难;
基于页寄存器(Page Registers)的实现方案
- 页表中,
index
存储了帧号,value
存储了标志位和页号;
这样存储使得表的大小与物理内存大小相关,与逻辑地址内存关联性减少;
- 反向页表中的表现都有一个关联的帧号和寄存器,寄存器内容:
redident bit
:此帧是否占用;occupier
:对应的页号p;protection bit
:保护位;
- 实例:
- 物理内存大小:
4k * 4KB = 16MB
; - 页面大小:
4096 bytes = 4KB
; - 页帧数:
4096 = 4k
; - 页寄存器使用空间(假设
8 bytes / register
):4096 * 8 bytes = 32 KB
; - 页寄存器的开销:
32 KB / 16 MB = 0.2%
; - 虚拟内存大小:任意;
- 物理内存大小:
- 优势:
- 转换表的大小相当于物理内存来说很小;
- 抓换表的大小跟逻辑地址空间的大小无关;
- 缺点:
- 需要的信息被对调,即根据帧号找到页号;
- 需要的信息被对调,即根据帧号找到页号;
基于关联内存(Associative Memory)的实现方案
-
页寄存器的硬件设计复杂,容量不大,需要放在CPU中;
-
解决方案:
- 如果帧数较少,页寄存器可以放置在关联内存中;
- 在关联内存中查找逻辑页号:
- 成功:帧号被提取;
- 失败:页错误异常(page fault);
- 限制因素:
- 大量的关联内存非常昂贵,难以在单个时钟周期内完成,耗电;
基于哈希的实现方案
- 当物理内存特别大的时候,反向页表也很大,访问一个地址可能需要遍历整个表(因为是按照物理地址建立的,需要挨个访问);
- 由于访问查找的比对是根据页号,而index是页帧号(ppn),所以只能从第一个页帧号开始,一个一个做比对(PID是当前进程的标识),速度较慢
- 解决方案:基于哈希的反向页表
- 上图中,
vpn
是页号,ppn
是帧号,offset
是偏移量,pid
是标识号; - 在程序进行访问时,CPU给出逻辑地址,为了防止哈希冲突,并给出了当前进程的PID作为标识,以确保找到正确的帧号;
- 将页号通过哈希计算(使用硬件加速),得到一个帧号,即反向页表的
index
,通过帧号,找到对应的表项; - 核对表现中的
PID
号是否与进程的PID
号一致;- 如果一致,则当前帧号为正确的帧号;
- 如果不一致,说明这里存在哈希冲突,则在表项中,寻找
NEXT
提供的下一个帧号;
通过该帧号,重复上述操作,直到PID
一致为止;
- 在有帧号和偏移量的情况下,便可以计算出对应的物理地址;