为什么需要非连续内存分配
连续内存分配有缺点
- 分配给程序的物理内存必须连续
- 存在外碎片 和 内碎片
- 内存分配的动态修改困难 (难以动态的增加内存和减少内存)
- 内存利用率低
之前连续物理内存地址分配 给进程分配内存 必须给其分配连续的物理内存区域 若内存中没有满足需要的内存大小的连续内存区域 分配内存就会失败
非连续内存分配目标
提高内存利用效率和管理灵活性
- 允许程序使用非连续物理内存空间
- 允许共享代码与数据 (两个进程共用一个函数库的时候)
- 支持动态加载和动态链接
非连续内存分配需要解决的问题
- 如何实现虚拟地址 -> 物理地址的转换 软件实现(开销大 灵活) 硬件实现(开销小 够用)
- 如何选择非连续分配中的内存块大小 段式存储(segmention 以段为单位 分配的时候这一个段在物理内存中必须是连续的) 页式存储(paging 以页为单位 页与页之间是不连续的)
段式存储管理
进程的段地址空间由若干个段组成
例如: 主代码段 公用库代码段 堆栈段 堆数据 数据段 符号表
段的逻辑地址空间是连续的 通过段式存储管理 各个段的实际物理内存可以是不连续的 分离的 以此实现非连续内存分配
段访问机制
段的概念
- 段 表示访问方式和存储数据 等属性 相同的一段地址空间
- 对应一个连续内存块
- 若干个段组成连续的逻辑地址空间
段基址先去查进程的段表里面 找到段描述符 找到段的起始地址和其长度 MMU(存储管理单元) 取出长度和偏移作比较 若非法 则内存异常 若合法 MMU 再利用其段基址和段偏移相加(不开分页的情况) 找到实际要访问的物理内存
页式存储管理
页式存储管理基本单位
物理页面:
页帧 (Page Frame)
- 把物理地址空间划分为大小相同的基本分配单位
- 大小 2的 n 次方 32位系统里 选择 4096 4k 作为页帧的大小 之所以要 选择 2的整数次幂 是因为在地址转换比较方便 在计算机中二进制移位 = 乘法的快速实现的方式
逻辑页面:
页 (Page)
- 把逻辑地址空间划分为大小相同的基本分配单位
- 帧和页的大小必须是相同的
页式存储管理机制的性能问题
- 访问一个内存单元需要2次内存访问 (内存访问性能问题)
- 第一次访问获取页表项
- 第二次访问获取数据
- 页表大小可能会很大
假设 32K的物理内容 1K 占一项目 32项 且 1项为 4个字节 共128个字节
64位机器 64位地址总线 如果每页 1024个字节 那么一个页表的大小为 2^64的内存 / 2^10 一页的大小 = 2^54个页面 假设每个页表项为 8 个字节 那么 就是 2^57
如何解决页式存储管理带来的性能问题
- 缓存 (Cachine) 缓存下页表项 下次访问有极大的可能性直接访问到物理内存 减少访问次数 就是下面的 TLB 快表
- 间接访问 (Indirection) 对长的页表 对它切段 就是下面的 多级页表
页到页帧
- 逻辑地址到物理地址的转换
页帧计算物理地址
页内偏移 == 帧内偏移
页号大小(连续的) != 帧号大小
不是所有页都有对应的帧
页号计算逻辑地址
- 页表结构
每个进程都有一个页表 - 每个页面对应一个页表项
- 随进程运行状态而动态改变 (页表项里面有属性 叫页表项标志)
- 页表基址寄存器 PTBR(Page Table Base Register)
乘上 2的n次方 就相当于 将帧号左移n位
- MMU/TLB
MMU 存储管理单元 就是上面图中所写的 段部件
TLB (Translation Look-aside Buffer) 快表
快表 实际上就是把近期访问过的页表项缓存到 CPU 里
- TLB 使用 关联存储(associative memory) 实现 具备快速访问性能
- 如果 TLB 命中 物理页号能很快取出
- 如果 TLB 不命中 则将对应的表项被更新到 TLB 中
快表虽然那么好 但是快表有容量限制和性能限制
多级页表
通过间接引用将页号分为 k 级 有效减少每级页表的长度
二级页表 逻辑地址转换物理地址 实例
反置页表
对于大地址空间 (64-Bits)系统 多级页表变得繁琐
反置页表思路
不让页表与逻辑地址空间的大小对应
让页表与物理地址空间的大小对应
反置页表优缺点
- 优点
- 页表大小相对于物理内存较小
- 页表大小与逻辑地址空间无关
- 缺点
- 页表信息对调后 需要根据帧号来找页号
- 在页寄存器里找逻辑地址的页号比较困难
页寄存器
包含:
- 使用位(Residence Bit):此帧是否被进程占用
- 占用页号(Occupier):对应的页号p
- 保护位(Protection Bits):约定这一页的访问方式,可读,可写……
页寄存器中的地址转换
CPU 生成的逻辑地址找到物理地址的方式 是通过 Hash 映射 来减少搜索的范围
反置页表的 地址转换的特别之处在于 它将进程的PID 也考虑进来
页寄存器示例
- 物理内存大小:40964096=4K4KB=16MB
- 页面大小:4096bytes=4KB
- 页帧数:4096=4K
- 页寄存器使用的空间:8*4096=32Kbytes(假定每个页寄存器占8字节)
- 页寄存器带来的额外开销:32K/16M=0.2%(大约)
- 虚拟内存的大小:任意
快表缓存页表项后的页寄存器搜索步骤
- 对逻辑地址进行 Hash 对换
- 在快表中查找对应页表项
- 有冲突时 遍历冲突项链表
- 若查找失败 产生异常
段页式存储管理
段式存储在内存保护方面有优势(因为同一个段的访问方式和存储的数据都是相同或者相类似的)
页式存储在内存利用和优化转移到后备存储方式有优势 (分了很多标准大小的块)
段页式存储管理的示例图在上面
段页式存储管理内存共享
通过指向相同的页表基址 实现进程间的段共享