不經意間看到這篇帖子,想起實習面試的時候再討論fork時,談到了這個問題。作一個記錄吧。html
原帖見:Linux寫時拷貝技術(copy-on-write)linux
在linux程序中,fork()會產生一個和父進程徹底相同的子進程,但子進程在此後多會exec系統調用,出於效率考慮,linux中引入了「寫時複製」技術,也就是隻有進程空間的各段的內容要發生變化時,纔將父進程的內容複製一份給子進程。面試
那麼子進程的物理空間沒有代碼,怎麼去取指令執行exec系統調用呢??數據結構
在fork以後exec以前兩個進程用的是相同的物理空間(內存區),子進程的代碼段、數據段、堆棧都是指向父進程的物理空間,也就是說,二者的虛擬空間不一樣,其對應的物理空間是一個。當父子進程中有更改相應段的行爲發生時,再爲子進程相應的段分配物理空間。若是不是由於exec,內核會給子進程的數據段、堆棧段分配相應的物理空間(至此二者都有各自的進程空間,互不影響),而代碼段繼續共享父進程的物理空間(二者的代碼徹底相同)。而若是是由於exec,因爲二者執行的代碼不一樣,子進程的代碼段也會分配單獨的物理空間。函數
在網上看到的還有個細節問題是:fork以後內核會將子進程排在隊列的前面,以讓子進程先執行,以避免父進程執行致使寫時複製,然後子進程執行exec系統調用,因無心義的複製而形成效率的降低。優化
如今有一個父進程P1,這是一個主體,那麼它是有靈魂也是有身體的。如今在其虛擬地址空間(有相應的數據結構表示)上有:正文段,數據段,堆,棧這四個部分,相應地,內核要爲這四個部分分配給自的物理塊。即正文段塊、數據段塊、堆塊、棧塊。至於如何分配,這是內核去作的事,在此不詳述。htm
內核:blog
(1) 複製P1的正文段,數據段,堆,棧這四個部分,注意是其內容相同。隊列
(2) 爲這四個部分分配物理塊,P2的:正文段(爲P1的正文段的物理塊,其實就是不爲P2分配正文段塊,讓P2的正文段指向P1的正文段塊),數據段(P2本身的數據段塊,爲其分配對應的塊),堆(P2本身的堆塊),棧(P2本身的棧塊)。以下圖所示,同左到右大的方向箭頭表示複製內容:進程
寫時複製技術:內核只爲新生成的子進程建立虛擬空間結構,它們複製於父進程的虛擬空間結構,可是不爲這些段分配物理內存,它們共享父進程的物理空間,當父子進程中有更改相應的段的行爲發生時,再爲子進程相應的段分配物理空間。
vfork的作法更加簡單粗暴,內核連子進程的虛擬地址空間也不建立了,直接共享了父進程的虛擬空間,固然了,這種作法就順水推舟的共享了父進程的物理空間
傳統的fork()系統調用直接把全部的資源複製給新建立的進程。這種實現過於簡單而且效率低下,由於它拷貝的數據也許並不共享,更糟的狀況是,若是新進程打算當即執行一個新的映像,那麼全部的拷貝將是無用功。
Linux的fork()使用寫時拷貝(copy-on-write)頁實現。寫時拷貝是一種能夠推遲甚至免除拷貝數據的技術。內核此時並不複製整個地址空間,而是讓父進程和子進程共享一個拷貝。只有在須要寫入的時候,數據纔會複製,從而使各個進程擁有各自的拷貝。也就是說,資源的複製只有在須要寫入的時候才進行,在此以前,只是以只讀方式共享。這種技術使地址空間的頁的拷貝被推遲到實際發生寫入的時候。
在頁根本不會被寫入的狀況下,舉例來講,fork()以後當即調用exec(),它們就無需複製了,fork()的實際開銷就是複製父進程的頁表以及給子進程建立惟一的進程描述符。在通常狀況下,進程建立後都會立刻運行一個可執行的文件,這種優化能夠避免拷貝大量根本不會使用的數據(地址空間經常包含數十兆的數據)。因爲Unix強調進程快速執行的能力,因此這個優化是很重要的,注:Linux COW和exec沒有必然聯繫