操作系统中的内存管理:内存分配算法(非连续内存分配)详解
1. 知识点描述
非连续内存分配是操作系统管理物理内存的一种重要方式,它允许一个进程的地址空间被分散地存放在不连续的物理内存区域中。这与连续内存分配(如分段、固定分区)形成对比。非连续分配的主要优势是能有效减少外部碎片,提高内存利用率。其核心实现依赖于两种关键技术:分页(Paging) 和分段(Segmentation)。本次讲解将重点剖析分页机制。
2. 为什么需要非连续内存分配?
- 问题(连续分配的局限): 在连续分配方案中,当一个进程需要被加载到内存时,系统必须为其找到一块足够大的连续空闲内存区域。随着进程的创建和终止,内存中会产生许多小的、不连续的空闲块(外部碎片),即使这些碎片的总和很大,也可能无法满足一个较大进程的请求,导致内存浪费。
- 解决方案(非连续分配): 非连续分配打破了“进程地址空间必须连续”的限制。一个进程可以被分割成多个小块,每个小块可以存放在内存中任何可用的位置。这样,那些零散的小空闲块都可以被充分利用起来。
3. 分页(Paging)机制详解
分页是实现非连续内存分配最主流的方法。其核心思想是将进程的地址空间和物理内存都划分为固定大小的、较小的单位,分别称为页(Page) 和页框(Page Frame,或物理块)。页和页框的大小完全相同(如4KB)。
步骤一:地址空间的划分
- 当一个进程被创建时,它的逻辑地址空间被操作系统自动分割成一系列页。例如,一个大小为64KB的进程,在4KB的页大小下,会被分成16个页(页0到页15)。
- 这些页在逻辑上是连续的(页0后面是页1),但它们可以被映射到物理内存中任意可用的页框里,这些页框在物理上是不连续的。
步骤二:建立映射关系 - 页表(Page Table)
- 核心问题: 既然页被分散存放,CPU在执行指令时,如何将一个指令中的逻辑地址(例如,“访问页0的第100个字节”)转换(映射)到正确的物理地址呢?
- 解决方案: 操作系统为每个进程创建一个专用的数据结构,称为页表(Page Table)。
- 页表结构: 页表本质上是一个数组。数组的索引是页号(Page Number),数组每个条目(称为页表项,PTE)中存储着该页对应的页框号(Frame Number),以及一些控制位(如存在位、读写权限位等)。
- 存在位(Present Bit): 表示该页当前是否在物理内存中。如果为0,则访问它会引发一个缺页中断(Page Fault)。
- 页框号(Frame Number): 这是映射的核心,指明了该页存放在物理内存中的哪个页框里。
步骤三:地址转换过程(逻辑地址 -> 物理地址)
这是分页机制最关键的环节。我们通过一个例子来理解。假设逻辑地址是16位,页大小是4KB(2^12字节)。
-
分解逻辑地址: CPU生成的逻辑地址被硬件(MMU,内存管理单元)自动分成两部分:
- 页号(p): 高4位(因为2^4 = 16页)。
- 页内偏移(d): 低12位(因为2^12 = 4096字节,即一个页的大小)。偏移量d指明了要访问的内容在页内的具体位置。
例子:逻辑地址 0x3A7F (二进制: 0011 1010 0111 1111)
p(高4位):0011(十进制 3,表示这是第3页)d(低12位):1010 0111 1111(十进制 2687,表示页内偏移2687字节处)
-
查页表: MMU使用页号
p作为索引去查找当前进程的页表。页表的起始地址存放在一个特殊的CPU寄存器中(如x86的CR3寄存器)。- 假设在页表中,第3项(页号p=3)的内容是“页框号=7”。
-
合成物理地址: 找到页框号
f(例如7)后,将其与页内偏移d拼接起来,形成最终的物理地址。- 物理地址 =
f(页框号) +d(页内偏移) - 由于页和页框大小相同,偏移量
d在物理页框内是直接适用的。 - 例子: 物理地址 = 页框7的起始地址 + 2687。如果页框从0开始编号,每个页框4KB,那么页框7的起始物理地址是 7 * 4096 = 28672。最终物理地址 = 28672 + 2687 = 31359。
- 物理地址 =
4. 分页机制的优缺点
- 优点:
- 几乎没有外部碎片: 内存分配以页框为单位,任何空闲页框都可以分配给需要的页,有效解决了外部碎片问题。
- 管理简单: 分配和回收的单位是固定大小的页框,易于管理。
- 支持虚拟内存: 通过“存在位”,可以轻松实现“请求调页”,即只有进程真正需要访问的页才被调入内存,这是虚拟内存的基础。
- 缺点:
- 内部碎片: 进程的最后一个页很可能用不完整个页框,造成页内空间的浪费。平均而言,每个进程会浪费半页的内存。
- 页表开销: 每个进程都需要一个页表,如果进程地址空间很大(如32位系统有4GB地址空间,页大小为4KB,则需要2^20个,即约100万个页表项),页表本身会占用大量内存。
- 地址转换性能问题: 每次内存访问都需要先查一次页表(一次额外的内存访问),这会使内存访问速度减半。
5. 针对缺点的优化技术
- 针对页表过大: 引入了多级页表(Multi-Level Page Table)。它将页表本身也进行分页,只将需要的一级页表和二级页表调入内存,大大减少了页表对内存的占用。
- 针对地址转换性能: 引入了转址旁路缓存(TLB, Translation Lookaside Buffer)。TLB是MMU中一块小型且高速的缓存,用于存放最近使用过的页表项(页号->页框号的映射)。当进行地址转换时,MMU首先在TLB中查找,如果找到(TLB命中),则无需访问内存中的页表,极大地加快了转换速度。
总结
非连续内存分配,特别是分页机制,是现代操作系统内存管理的基石。它通过将进程和内存划分为固定大小的单元,并利用页表维护映射关系,巧妙地解决了外部碎片问题,并为实现强大的虚拟内存功能铺平了道路。尽管存在内部碎片和性能开销,但通过多级页表和TLB等优化技术,这些缺点得到了有效缓解。理解分页是深入理解虚拟内存、进程隔离等高级概念的关键一步。