K03 — Memory Copy During Process Creation: copy_page_range and COW Details
Key Insight: The elegance of fork lies in turning “copying” into “sharing, until it must be otherwise”.
Memory Copy Flow in fork
The core of fork is copy_process. The memory-related part is copy_mm, which calls copy_page_range to copy the parent’s address space.
fork 内存复制的调用链:
sys_fork() → do_fork() → copy_process()
→ copy_mm() → dup_mm()
→ dup_mmap() → copy_page_range()
→ copy_pud_range() → copy_pmd_range() → copy_pte_range()copy_page_range iterates through each VMA of the parent process, creating corresponding page table entries for the child:
copy_page_range() 的核心逻辑:
int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *vma)
{
unsigned long addr = vma->vm_start;
unsigned long end = vma->vm_end;
for (; addr < end; addr += PAGE_SIZE) {
// 1. 为子进程分配新的 PTE
// 2. 复制 PTE 内容
// 3. 设置 COW 标志(只读)
// 4. 共享物理页面(引用计数 +1)
}
}COW Implementation in fork
fork 时 COW 的关键步骤:
1. 复制页表条目(PTE),但共享物理页面
- 父 PTE:物理页面 + RW 位
- 子 PTE:同样的物理页面 + RO 位(只读)
- 物理页面的引用计数 +1
2. 设置页面为只读
- 父子都有页表指向同一个物理页
- 页面被标记为只读
- 当任一方写入时,触发 page fault
3. Page Fault 处理 COW
- Page fault handler 发现:
a. 页面存在
b. 只读
c. 引用计数 > 1(共享)
- 分配新页面
- 复制旧页面内容
- 建立新页面的映射
- 把写入方的 PTE 更新为新页面 + RW
关键数据结构:
struct page {
atomic_t _refcount; // 引用计数
// ...
unsigned long flags; // PG_dirty / PG_writeback 等
};
PG_referenced 标志:
- 页面最近被访问
- COW 时检查这个标志
- 如果是 clean + referenced,优先不回收Special COW Handling for File Mappings
文件映射的 fork COW 与匿名映射不同:
匿名映射(MAP_ANON):
- 页面内容是内存(全是零或匿名数据)
- COW 时直接分配新页,复制内容
- 不需要写回文件系统
文件映射(MAP_FILE):
- 页面内容来自文件
- COW 时需要先读取文件内容到新页
- 写时复制后变成"私有"(不再同步到文件)
writeback 处理:
- COW 后的脏页不会写回原文件
- 只对当前进程可见
- 进程退出时释放(不回写文件)
MAP_SHARED 的处理:
- fork 时不复制页表(共享同一个 VMA)
- 父子共享同一个 page cache
- 写入会直接修改 page cache(立即对其他进程可见)
- 不是 COW,是真正的共享Special Handling of vfork and clone
vfork(virtual fork)的特殊处理:
vfork() 和 fork() 的区别:
fork():
- 复制整个地址空间(COW)
- 父子可以独立运行
- 写时复制,内存可能翻倍
vfork():
- 完全共享父进程地址空间(不复制)
- 子进程先运行,调用 exec 或 _exit 后父进程才继续
- 效率更高(但有风险)
- 现代 Linux 实际用 clone(CLONE_VM) 实现
clone(CLONE_VM) 的效果:
- 父子共享同一个 mm_struct
- 没有 COW,没有页表复制
- 共享所有 VMA 和页表
- 立即 exec 时效率最高(不复制任何内存)
vfork 的风险:
- 如果子进程不调用 exec/_exit,父进程无法运行
- 子进程修改内存会影响父进程(完全共享)
- 现代代码很少用 vfork,fork + COW 更安全Summary
- fork Memory Copy: copy_page_range iterates VMA; copies page tables but shares physical pages; marks read-only (COW)
- COW Implementation: PTE read-only + page refcount +1 → write triggers page fault → allocate new page → copy content → update PTE
- Anonymous vs File Mapping: Anonymous COW copies directly; file COW reads file content into new page (privatization)
- MAP_SHARED: Not copied on fork; parent and child share the same page cache; writes are immediately visible to other processes
- vfork: Fully shares address space (no copy); child runs first until exec/_exit; modern systems use clone(CLONE_VM)
Next (K04): When memory is exhausted, how does the kernel choose which process to kill? OOM Killer scoring mechanism and cgroup memory limits.
Comments