## Copy-on-Write 如果我們要快速建立一個 process,其中一個方法就是 demand paging(有用到的 page 才 load 進 memory),但是如果是用``fork()`` 來建立 process的話,還有另一個類似 page sharing 的方法,既可以免除 demand paging 的需求,又能快速的用最少量的 pages,來 allocate 給新建立的 process 回顧一下 ``fork()`` 這個 system call 會 create 一個和 parent 幾乎一模一樣(除了 pid)的 child process 傳統上要如何做到這件事,就是透過直接複製 parent 的 pages,來建立一份和 parent 相同的 address space 給 child 可是這裡就有一個問題,就是實際上很多 child process 在被建立後不久,馬上就會用 ``exec()`` 去把某個 program load 到它的 address space 來去做別的事,也就蓋掉沒多久前建立時從 parent 那邊複製過來的東西,所以實際上花那麼多時間去 copy 和 parent 相同的東西就有點沒必要了 從這個出發點,我們就會想說乾脆一開始就不要去做這件事,不要多花力氣去 copy 一份一模一樣的東西,child 跟 parent 一開始就先共用那些 parent 原本就有的 pages 就好啦! 這個「child 跟 parent 一開始先共用 pages」的技術,就叫做 <font color = "snake">Copy-on-Write</font>,而這些共用的 pages 也就叫做 <font color = "snake">copy-on-write pages</font> --- 可是,如果這時候 parent 和 child 有其中任何一個去寫入這些 pages 呢? 那因為我們有先標記這些 pages 和一般 pages 不同,是 copy-on-write pages,在發生這種情況的時候,被寫到的 shared page 就會有一份 copy 被建立出來 舉例來說: > 假設有一個 child proces 想要去對某個含有部分 stack 的 page 做變更,然後這個 page 剛好是 copy-on-write page,那 OS 就會去 free frame list 找一個可以使用的 frame,然後複製一份等一下要 child 被更改的 page 的 copy,再把這個新的 copy map 到 child 的 address space,這樣一來,child 在對這個 page 做修改的時候就不會變更到 parent 原本的 page 了! ![image](https://hackmd.io/_uploads/HygR2MAdT.png) > 假設這個時候 $process_1$ 想去修改 page C,OS 就會去建立一份 page C 的 copy,再 map 給 $process_1$ 讓 $process_1$ 去改 > 變成下圖: ![image](https://hackmd.io/_uploads/SyWP6GROT.png) 在這樣的做法之下,只有有被修改過的 pages 需要額外複製一份,其他沒有更動的 pages 就保持共享就好了 - 只有可以被修改的 pages 需要被標註為 copy-on-write pages,如果是本來就不能被 modified 的 pages,例如說 executable code,就直接 share 就好 - 會用到 copy-on-write 技術的 OS 舉例來說有: Windows, Linux, MacOS --- ### vfork 我們已經知道可以用 ``fork()`` 加上 copy-on-write 的這個方法了,但除此之外,有些版本的 UNIX 提供了另一種變型的 ``fork()`` 叫做 <font color = "snake">``vfork()``</font> (即<font color = "snake"> virtual memory fork</font>) ``vfork()`` 和 ``fork()`` 加上 copy-on-write 不太一樣,``vfork()`` 的特點就是<font color = "red">沒有使用 copy-on-write</font> 那我們什麼情況需要用到 ``vfork()`` 呢? 像是前面說到的 child 在被建立之後馬上就會執行 ``exec()`` system call 的情況就非常適合,因為 child 會直接去把別的 program load 進來,所以當然也不會去修改到 parent 的 pages 的內容,也就不需要 copy-on-write 啦! 因此,如果是用 ``vfork()``, child 會直接用 parent 的 address space,同時 parent 被 suspended,如果 child 有改到任何 parent 的 pages,在 parent 從 suspend 恢復時,parent 就看得到 child 對哪些 pages 做了哪些變更,但實際上 ``vfork()`` 是拿來像前面所說的功能,所以基本上也不會讓 child 去改動到 parent 的東西 除此之外,因為 child 直接用 parent 的 address space,所以建立時也不用複製任何東西,所以很有效率!所以除了建立 process 以外,``vfork()`` 有時候也會被拿來 implement UNIX 的 command-line shell interface --- ## Reference - 恐龍 10.3