#Linux内存管理# 浅析缺页中断中私有映射且发生写时复制COW的工作原理
案例背景
假设一个进程通过 mmap 系统调用映射了一个文件(data.txt),并设置 私有映射(MAP_PRIVATE)。随后进程尝试修改映射区的数据,此时触发写时复制(Copy-On-Write, COW)机制。
关键步骤解析
1.初始文件内容
data.txt 原始内容:
| A | B | C | D |
物理内存中的页缓存(Page Cache)存放文件内容:
物理页1: [A, B]
物理页2: [C, D]
2.建立私有映射
进程执行:
void *addr = mmap(
NULL,
4096, // 映射4KB
PROT_READ | PROT_WRITE, // 可读写
MAP_PRIVATE, // 私有映射
fd, // data.txt的文件描述符
0 // 文件偏移0
);
此时状态:
进程的页表指向物理页缓存(页1和页2)。
页表权限:设置为只读(即使指定了PROT_WRITE,内核会在COW前保持只读)。
3.读取数据(无COW)
进程读取 addr[0](值为 A):
直接访问物理页缓存(页1),无需复制。
无缺页中断,读取速度高效。
4.修改数据(触发COW)
进程尝试修改数据:
addr[0] = 'X'; // 尝试将A改为X
COW流程:
步骤1:CPU检测到写操作,但页表权限为只读 → 触发 缺页中断。
步骤2:内核分配新物理页(页3):
物理页3: [A, B] // 复制页1的内容
步骤3:更新进程页表
原虚拟地址指向 新物理页3(原页1不再直接映射)。
页表权限改为 可读写。
步骤4:重新执行写操作,将页3的内容修改为:
物理页3: [X, B] // A被改为X
最终状态
物理内存:
页1(原始页缓存): [A, B] // 未被修改,仍供其他进程读
页2: [C, D] // 未被访问,保持原样
页3(COW新页): [X, B] // 进程私有副本
文件data.txt内容不变(仍为A,B,C,D)。
进程看到的虚拟地址内容:[X, B, C, D]。
COW的核心机制
步骤 内核操作 物理内存变化
写操作触发中断 CPU检测到只读页的写操作 → 缺页中断 无变化
分配新物理页 内核分配新页,复制原页内容 新增页3(内容复制自页1)
更新页表 修改页表:指向新页 + 权限改为可读写 进程与页1解耦,绑定页3
执行写操作 修改新页内容 页3内容更新(A→X)
COW的优势
1.节省内存
未修改的页(如页2)保持共享,减少冗余复制。
2.高效惰性复制
仅在实际写入时触发复制,避免提前拷贝的开销。
3.数据隔离
进程修改不影响原始文件或其他进程(私有映射特性)。
4.减少I/O
写操作延迟到缺页中断时才处理,避免不必要的磁盘写入
关键代码验证
// 在修改数据后验证文件内容不变
printf("进程内存: %c\n", addr[0]); // 输出 'X'
lseek(fd, 0, SEEK_SET);
char file_buf[4];
read(fd, file_buf, 4);
printf("文件内容: %c\n", file_buf[0]); // 输出 'A' (未修改)
注意:私有映射的修改对文件不可见,对共享该文件的其他进程也不可见。