1. 写时复制(COW)技术解析
写时复制(Copy-On-Write)是操作系统内存管理中的一项关键技术,它通过延迟物理内存的复制操作来优化系统性能。在传统的进程复制(fork)操作中,内核会立即为子进程分配新的物理内存,并将父进程的内存内容完整复制一份。这种方式虽然简单直接,但存在两个明显缺陷:
- 内存浪费:大多数情况下子进程会立即执行exec加载新程序,导致复制的内存被完全丢弃
- 性能损耗:复制大内存空间需要消耗大量CPU时间和内存带宽
COW机制的精妙之处在于它打破了这种"要么全复制,要么不复制"的二元思维。具体实现上,当父进程调用fork创建子进程时:
- 内核仅为子进程创建与父进程完全相同的页表结构
- 将父子进程的所有页表项标记为只读(清除PTE_W标志)
- 新增自定义的COW标记位(如PTE_C)
- 增加被共享物理页的引用计数
这种设计使得父子进程可以共享相同的物理内存,直到任一进程尝试修改共享页面时,才会触发真正的内存复制。这种"按需复制"的策略显著提升了系统效率,特别是在以下场景:
- 服务器程序中频繁fork短生命周期子进程
- 进程创建后立即执行exec的情况
- 大内存应用的快速克隆
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心数据结构改造
2.1 物理页引用计数机制
在xv6的原始实现中,物理内存管理通过简单的空闲链表(freelist)完成,缺乏对共享页面的追踪能力。我们需要在kalloc.c中扩展内存管理结构:
c复制struct {
struct spinlock lock;
struct run *freelist;
int memlist_cite[PHYSTOP/PGSIZE]; // 引用计数数组
} kmem;
这个改造引入了三个关键设计决策:
-
引用计数与锁的整合:将计数数组与freelist放在同一结构体中,使用同一把自旋锁保护,确保:
- 分配/释放操作的原子性
- 引用计数修改的原子性
- 避免单独锁带来的死锁风险
-
数组索引计算:通过
(uint64)pa/PGSIZE将物理地址转换为数组索引,这种计算方式:- 保证每个物理页都有唯一的计数槽位
- 计算开销极小(仅需移位操作)
- 与现有分页机制完美契合
-
初始化处理:在kinit()中增加计数数组清零操作:
c复制void kinit() {
initlock(&kmem.lock, "kmem");
memset(kmem.memlist_cite, 0, sizeof(kmem.memlist_cite));
freerange(end, (void*)PHYSTOP);
}
2.2 内存分配/释放的改造
kalloc()需要为每个新分配的页面初始化引用计数:
c复制void *kalloc(void) {
struct run *r;
acquire(&kmem.lock);
r
