内存管理将分成三个方面来讨论:
XV6 内存管理整体上说比较简单,因此顶层的内存管理代码很容易掌握。但是由于 X86 硬件的地址部件复杂性,使得底层与 CPU 地址部件交互的代码显得比较繁杂。
X86 启动时处于 16 位状态并且没有分页机制,寻址空间为 0~2^20-1
(共 1 MB),然后启动扇区的代码将很快进入保护模式将寻址空间扩展到 0~2^32-1
(共 4 GB)并开启分段地址模式,内核主体代码运行后则启动分页机制。因此整个启动过程经历了三种不同的地址模式。
需要特别提到的是,虽然 X86 处理器设计的很复杂,进入保护模式后既有分段又有分页机制,但是 XV6 仅利用 X86 的分段机制作为特权级控制,使用两个用户段(代码和数据各一个)和两个内核段(代码和数据各一个)。这四个段都设置为从 0 地址开始,因此 X86 下的逻辑地址和线性地址是一致的,相当于架空了分段机制的地址映射功能(保留了特权控制能力)。ARM 处理器和 MIPS 处理器只使用分页机制而没有分段机制,相对而言它们的内存管理代码比较简洁。
启动分页机制后,空闲的物理页帧是按照链表方式管理的。每个空闲物理页帧起始 4 个字节用作指针,链表头由 kmem.freelist 所管理。当需要分配物理页帧时,通过 kalloc() 从链表头部取下一个页帧,反之释放一个页帧后通过 kfree() 加入到该空闲列表中。
XV6 的进程在启动时的内存空间使用情况由可执行文件中的信息决定,运行后还可能申请新的空间或释放内存空间,呈现动态变化。XV6 进程空间的内存管理也比较简单,只是一个连续区间的伸缩变化,而不像 Linux 进程那样会出现多个离散的区间。