Operating system 在写时复制期间,父进程是否会丢失写功能?

Operating system 在写时复制期间,父进程是否会丢失写功能?,operating-system,fork,paging,cpu-architecture,copy-on-write,Operating System,Fork,Paging,Cpu Architecture,Copy On Write,假设我们有一个特定的父进程,在内存中存储了任意数量的数据,我们使用fork生成一个子进程。我理解,为了让操作系统执行写时复制,内存中包含我们正在修改的数据的特定页面将设置其只读位,当孩子试图修改数据,将整个页面复制到内存中的另一个区域,以便孩子获得自己的副本时,操作系统将使用异常。我不明白的是,如果内存中的特定部分被标记为只读,那么数据最初所属的父进程也将无法修改数据。那么整个计划如何运作呢?父进程是否失去了对其数据的所有权,即使父进程自己试图修改数据,也必须执行写时复制?正确,如果任一进程写入

假设我们有一个特定的父进程,在内存中存储了任意数量的数据,我们使用
fork
生成一个子进程。我理解,为了让操作系统执行写时复制,内存中包含我们正在修改的数据的特定页面将设置其只读位,当孩子试图修改数据,将整个页面复制到内存中的另一个区域,以便孩子获得自己的副本时,操作系统将使用异常。我不明白的是,如果内存中的特定部分被标记为只读,那么数据最初所属的父进程也将无法修改数据。那么整个计划如何运作呢?父进程是否失去了对其数据的所有权,即使父进程自己试图修改数据,也必须执行写时复制?

正确,如果任一进程写入COW页面,则会触发页面错误

在页面错误处理程序中,如果页面应该是可写的,它将分配一个新的物理页面并执行
memcpy(newpage,shared_page,pagesize)
,然后更新出现故障的进程的页面表,以将新页面映射到该虚拟地址。然后返回用户空间以重新运行存储指令

这对于像
fork
这样的东西来说是一个胜利,因为一个进程通常在触摸一页(堆栈内存)后立即进行
execve
系统调用
execve
销毁该进程的所有内存映射,有效地将其替换为新进程。
父进程再次拥有每个页面的唯一副本。(除了写时已经复制的页,例如,使用
mmap
分配的内存通常映射到一个零物理页,因此读取可以在L1d缓存中命中)

聪明的优化方法是,
fork
实际复制包含堆栈顶部的页面,但仍然对所有其他页面执行lazy COW,前提是子进程通常会立即执行
execve
,从而删除对所有其他页面的引用。不过,临时将所有页面翻转为只读并返回,仍然需要父级中的TLB失效

一些UNIX实现在两者之间共享程序文本 这是无法修改的。或者,孩子可以分享所有的信息 父级的内存,但在这种情况下,内存是共享的
写时复制
,这意味着无论何时两者中的任何一方想要修改内存的一部分,该内存块都会被显式删除 首先复制以确保修改发生在专用内存中 区域

摘自:


如果父母在修改了一些数据后决定生育第二个孩子,那该怎么办?如果父级失去对其数据的所有权,那么现在将有两个只读内存段。一个在产生第一个子代之后,另一个在产生第二个子代之后。如果父系统决定再次修改其数据,那么操作系统现在必须为父系统分配第三个可写段,以便在写入时进行复制。这听起来不对,因为这似乎是对资源的严重浪费。@user3769877:只要在子进程中运行
execve
,所有内存映射都会被清除,因此所有父进程页上的引用计数都会降回1。(除非它们已经共享)。COW实际复制的任何页面的子副本都会在
execve
上释放,无论是父级还是子级导致了复制。但是如果从未调用exec会怎么样呢。也就是说,我们只需调用fork一次,在父级中更改一些数据,然后第二次调用fork,在父级中更改数据,第三次调用fork。。。等等等等。在这种情况下,写时复制将如何工作?@user3769877:那么所有页面都将被共享,除了少数已更改的页面。每一个改变页面的进程都会有它自己的页面副本,就像什么都没有被共享一样。与在fork上复制所有内容相比,您节省了大量内存,例如,如果
bash
foo之类的命令创建子shell;(echo 1234>file)&bar
,运行
echo>file
的子shell不会修改它的大部分页面,因此需要一些额外的页面错误来避免大量复制工作。(在设计Unix时尤其如此,当时mem复制速度比pagefault慢)