OSTEP / Virt-段式系统

OSTEP

段式分配

虚拟内存实现需要硬件支持. 带有这种虚拟地址到物理地址转换功能的硬件, 叫做内存管理单元(MMU). 通常MMU集成在CPU中.

从最粗犷分配场景开始, 我们假设:

我们只需要MMU提供两个寄存器base和bound. 顾名思义. 转换过程就非常简单, 对于所有内存访问base + offset. 如果地址超过bound产生异常. 每个进程都有自己一个(base, bound)组合, 存储在PCB(process control block)中. 进程被调度时从PCB中恢复.

这种方式就暂且叫做动态分配(dynamic allocation). 这种方式简单但是有两个缺点: 1. 空间浪费严重. 如果declare内存8KB那么就要分配8KB在物理内存上, 即使8KB中只使用了很少部分 2. 没有办法执行那些使用内存比物理内存大的进程. (两层意思: a. declare内存比物理内存大, 但是实际使用到不了物理内存. b. 实际使用就会超过物理内存. 其中b不需要swapping而a需要). 然后进入segmentation allocation.

段式分配(segmentation allocation). 如果一个进程declare 8KB, 但是实际只使用1KB code + 1KB stack + 2KB heap = 4KB的话, 那么其实我们可以将其拆分成为3个段(code seg, stack seg, heap seg), 每个段和动态分配一样分配(base, bound)来标识这个段如何来做转换. 如何判断一个地址对应哪个段呢? 通常是根据地址头部几个bits来判断. 每个段除了(base, bound)之外, 还有一些额外信息如: a. 地址增长方向. 比如stack可能反向增长 b. 这个段读写权 等. 分段配可以是粗粒度的(只有有限的几个段), 也可以是细粒度的(编译器可以自行将代码数据等拆分成为多个段. 虽然更加复杂, 但是设计者认为可以提高内存使用效率)

段式分配解决了动态分配中空间浪费严重问题. 动态分配空间浪费是因为内部碎片引起的. 但是段式分配同样也会引入空间浪费问题. 因为外部碎片引起的空间浪费. 段式分配的段大小并不是固定的, 所以在分配和释放过程中会出现内存碎片. 一个解决办法是做compaction. 但是在此之前, 应该尽可能地从分配策略(Free Space Management)来少减少/延缓内存碎片产生. 许多操作系统教科书里面花了许多篇幅讨论这些策略. 但是然并卵, 因为这些策略在现在基于分页(paging)的虚拟内存管理中没有任何作用.