操作系统之 物理内存管理 非连续内存分配

为什么需要非连续内存分配

连续内存分配有缺点

  • 分配给程序的物理内存必须连续
  • 存在外碎片 和 内碎片
  • 内存分配的动态修改困难 (难以动态的增加内存和减少内存)
  • 内存利用率低

之前连续物理内存地址分配 给进程分配内存 必须给其分配连续的物理内存区域 若内存中没有满足需要的内存大小的连续内存区域 分配内存就会失败

非连续内存分配目标

提高内存利用效率和管理灵活性

  • 允许程序使用非连续物理内存空间
  • 允许共享代码与数据 (两个进程共用一个函数库的时候)
  • 支持动态加载和动态链接

非连续内存分配需要解决的问题

  • 如何实现虚拟地址 -> 物理地址的转换 软件实现(开销大 灵活) 硬件实现(开销小 够用)
  • 如何选择非连续分配中的内存块大小 段式存储(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%(大约)
  • 虚拟内存的大小:任意
快表缓存页表项后的页寄存器搜索步骤
  1. 对逻辑地址进行 Hash 对换
  2. 在快表中查找对应页表项
  3. 有冲突时 遍历冲突项链表
  4. 若查找失败 产生异常

段页式存储管理

段式存储在内存保护方面有优势(因为同一个段的访问方式和存储的数据都是相同或者相类似的)

页式存储在内存利用和优化转移到后备存储方式有优势 (分了很多标准大小的块)

段页式存储管理的示例图在上面

段页式存储管理内存共享

通过指向相同的页表基址 实现进程间的段共享