蔚淦丞
发布于 2026-09-13 / 6 阅读
0
0

写时复制

核心思想

fork 创建子进程时,不拷贝物理内存,父子进程虚拟页映射到同一份物理页;仅当任意进程尝试写入页面时,才复制对应的物理页。

页面分类与 fork 处理

  1. .text.rodata:本身 PTE 标记只读,不参与 COW。父子共享物理页;尝试写直接触发段错误 SIGSEGV。

  2. .data.bss、堆、用户栈:属于可写页面。fork 时,内核批量修改父子双方对应的 PTE:R/W 置 0(临时只读陷阱),物理页引用计数 + 1,开启 COW。

COW 触发流程

  1. 父 / 子进程写页面,MMU 检查 PTE,R/W=0,触发写保护异常

  2. 内核读取该物理页的引用计数:

    • 引用计数 > 1:还有多个进程共享此页。分配新物理页,把旧页内容拷贝到新页;修改当前进程 PTE,指向新页,R/W 改为可写;旧页面保留给另一个进程。

    • 引用计数 = 1:只剩当前进程使用该物理页,无需拷贝页面,直接把当前 PTE 的 R/W 改成可写。

  3. 程序恢复执行。

关键细节

  1. 权限标记全部存放在 PTE,物理内存条本身没有读写权限。

    • .text/.rodata 的只读:永久业务只读,写直接段错误。

    • COW 的只读:临时陷阱,目的捕获写操作,页面本身支持读写。

  2. TLB 会缓存 PTE 权限位;修改 PTE 之后,必须失效对应 TLB 条目,避免旧缓存干扰。

  3. COW 复制粒度:以单个页(4KB)为单位,不是一次性复制整个进程内存。

总结

fork 只复制页表,共享物理内存;可写页面 PTE 临时设为只读捕获写操作。写页面触发异常,内核依据物理页引用计数判断:多进程共享则复制页面;单独持有则直接放开写权限。只读代码段不参与写时复制。


评论