我们了解分叉后的 COW 行为(例如所描述的这里)如下:fork 为子进程创建父进程页表的副本,并将物理页标记为只读,因此如果两个进程中的任何一个尝试写入,都会触发页错误并复制该页。
子进程执行后会发生什么?我们假设父进程可以再次写入其页面而不会触发页面错误,但事实证明很难找到有关如何实现这一点的准确信息。
欢迎任何指针(包括代码)!
答案1
当子进程执行时,其所有当前页面都将替换为与新的可执行映像(加上堆、堆栈等)相对应的一组全新页面。
现代操作系统通过维护父进程和子进程之间共享的物理页的引用计数来实现 CoW。如果父进程和子进程之间共享页面,则引用计数将为 2。一旦子进程执行 exec,共享页面的引用计数就会递减(例如,返回到 1),因此父进程的任何写入操作没有 CoW 也会成功。
为了您的娱乐,创建一个简单的程序,执行 fork,然后子进程休眠几秒钟,然后执行并执行。现在观察/proc/PID/smaps
fork 之前(当然只有父进程)、fork 之后但 exec 之前和 exec 之后两个进程的内容。注意Shared_XXX页面和相应的地址范围。
在代码方面,有一些简单的 XV6 扩展来支持写时复制。简单的谷歌搜索可能就足够了。另一个值得一看的地方可能是https://github.com/torvalds/linux/blob/master/kernel/fork.c。从叉子入口开始追踪它并享受乐趣。
Fork 相当简单,一旦你掌握了它的窍门,但内存管理可能会很麻烦。请参阅“mm/memory.c”:“copy_page_range()”