Contents

内存管理:概述与介绍(译文)

本文译自 OSDev Wiki 上的一篇文章,原文链接为 Memory management - Overview and Introduction

内存管理是任何操作系统内核中的关键组成部分。为程序提供一种快速、可重复地分配和释放内存的机制是内核的一项主要职责。针对物理内存的分配存在多种实现方法,包括位图法、伙伴分配算法,以及基于树结构或队列/栈的内存管理方式。

有关内存分配模型和内存分配方法的概述,请参考程序内存分配类型。若需要了解堆式内存管理(即在非大块边界上分配较小内存块),请参阅相关内容。通常情况下,堆不仅存在于内核中,也存在于应用程序中,并通过标准库的形式提供。关于自动内存管理方法的讨论,请参考垃圾回收相关内容。


地址空间(Address Spaces)

许多平台(包括 x86)使用内存管理单元(MMU)来处理虚拟地址空间与物理地址空间之间的转换。一些体系结构将 MMU 集成在处理器内部,而另一些则使用独立芯片。使用多地址空间可以让每个任务拥有独立的内存空间。在现代系统中,这对内存保护至关重要,将各进程的内存空间隔离可以防止它们相互干扰。

物理地址空间(Physical Address Space)

物理地址空间是访问 RAM 中真实物理位置时所使用的直接内存地址。该空间中的地址是用于在地址总线上标识内存位置的比特模式。

在该内存模型下,每个可执行文件或库必须使用 PIC(位置无关代码),或携带重定位表,以便加载器能够调整跳转与分支目标地址。

AmigaOS 使用过这种内存模型,因为早期的 680x0 CPU 缺乏 MMU。该模型具有较高的效率,但无法为进程提供彼此间的内存保护,因此在现代桌面操作系统中已被视为过时。此外,该模型也容易造成内存碎片。不过,一些嵌入式系统仍可能采用这种方式。

虚拟地址空间(Virtual Address Space)

MMU(内存管理单元)的出现使得可以使用虚拟地址。虚拟地址可以映射到任意物理地址。这样可以为每个可执行程序提供独立的地址空间,使其内存起始地址总是从 0x0000 0000 开始,从而减少加载器的重定位工作。同时,这有效解决了内存碎片问题,因为不再需要分配物理上连续的内存块。此外,由于内核控制虚拟到物理的映射,进程只能在内核允许的情况下访问其他进程的内存空间,从而实现了隔离与保护。


内存映射系统(Memory Translation Systems)

在现代计算机系统中,x86 平台较为独特,它拥有两种用于实现虚拟地址到物理地址映射的方法。这两种方法分别是分页(paging)和分段(segmentation),它们在内存映射管理机制上存在明显差异。

分段(Segmentation)

主条目:Segmentation

除了 x86 体系结构外,分段机制在主流系统中并不常用。在保护模式下,这种内存管理方式将一个进程的内存划分为多个独立的段,并通过段寄存器进行管理。常见的段寄存器包括:CS、DS、SS、ES、FS、GS,分别对应代码段、数据段、栈段及其他扩展段(CodeSegment, DataSegment, StackSegment, the rest are ExtraSegments)。

分页(Paging)

主条目:Paging

为每个虚拟地址单独建立映射显然效率低下。传统的虚拟内存实现方式将物理内存划分为若干固定大小的页(page),并以页为单位建立虚拟地址到物理地址的映射。该映射工作主要由 MMU 完成,因此其性能开销较低,并且被普遍认为是为实现内存保护所付出的合理代价。

/images/内存管理/概述与介绍/image.png


虚拟内存(Virtual Memory)

在物理内存耗尽时,系统并不会直接报告“内存不足”,而是会将当前未被访问的页面写入硬盘(交换文件或交换分区),从而释放对应的物理内存页。这一过程称为“页面换出”(paging out)。

这种机制需要额外的管理和调度逻辑。当进程访问被换出的页面时,系统必须将其从硬盘重新加载到内存中(换入),这会带来严重的性能损耗。因此,要想让虚拟内存高效运行,需要非常精心的设计。如果设计不当,这一部分将成为操作系统性能的主要瓶颈。

另一方面,虚拟地址空间的大小不再受限于实际物理内存,而是取决于 CPU 地址能力和硬盘容量。从概念上讲,CPU 缓存和 RAM 成为硬盘之上的缓存层,而硬盘则成为“真正的”内存容量上限。

页面置换系统依赖的基本假设是:一个进程在任意时刻并不需要其全部内存,而只需要其中的一部分(类似抄写一本书时,你并不需要整本书和整叠空白纸,你只需要当前章节和一些空白页,只要有人能在你写完时替你保存已完成页、并及时为你提供下一章节和新的空白页即可)。这个可运行所需的内存子集称为工作集(working set)。一个进程至少要有其工作集对应的物理页面才能正常运行。如果提供的页面少于其工作集大小,就可能出现严重的抖动(thrashing)现象,即进程不断请求换入页面,而换入操作会迫使工作集中的其他页面被换出,导致系统陷入频繁交换,性能急剧下降。

注意:页面交换并不是唯一方案,系统也可以进行段交换或进程交换。在这些方案中,交换粒度更大,通常由用户态软件控制,这增加了应用开发难度,并导致交换过程耗时更长,因为被交换的内容远大于 4KB 页面大小。

另一点:主流桌面操作系统通常使用“预取式(speculative)换入”策略,通过一次性加载超过实际需求的页面来降低缺页率(page miss)。由于大多数程序的访问具有局部性,并且磁盘读取一整条磁道通常比多次离散读取更快,因此这种策略在实践中往往能带来性能收益。