研究一下 fork 的原理,而且還有 vfork 的一些使用場景

筆者一直試圖從最基本的原理上去理解(甚至嘗試原理性設計)一個服務器的架構,爲此提出了一些問題。此外,筆者對 異步 I/O 也有很多學習。從幾個方面學習了 vfork() 的用法。html

本文純粹記錄一下。不過不一樣於其餘資料的大段代碼,本文更多地用文字和排版來儘量清晰地說明。linux

本文地址:http://www.javashuo.com/article/p-suwxuwld-cq.html程序員

Reference:

linux網絡編程之socket(四):使用fork併發處理多個client的請求和對等通訊p2p
fork和vfork的區別編程

fork 和 vfork

fork() 應該說是 UNIX 和類 UNIX 系統中最古老和最原始的建立子進程的系統調用了。甚至能夠說,fork 不只僅是建立子進程,而更多的是建立進程的主要手段。除了 init 進程以外,因此進程都是從 init 派生出來的。不少進程均可以是由 init 或者是 bash 的子進程,但這種狀況下,咱們已經不把它們做爲子進程來看待了。segmentfault


fork( )

簡單一句話,fork()的作法就是拷貝父進程的上下文,而後再從父進程中分離出來。其實可能大部分程序員對 fork() 的用法是:建立子進程,而後父子進程使用 pipe 通訊。bash

fork 複製的上下文與父進程的關係

在實際使用中,確實就按照上文而言,fork() 以後的子進程對父進程是拷貝關係。也就是說,子進程對大多數變量的操做,都是不會影響父進程的。服務器

可是文件描述符等涉及操做系統層面的全局資源須要注意:這些僅僅是上下文在內存中的位置不一樣,可是它們底層所指向的資源是共享的,操做時須要注意。不過事實上,這也是父子進程使用 pipe 互相通訊的原理基礎。網絡

fork 複製上下文的原理

我被問過一個問題:「fork() 調用時要拷貝上下文,這麼作在大進程中調用的時候是否是效率很低?」架構

Linux 做爲開源運動的集大成之做,顯然不會那麼蠢。事實上,調用 fork 以後,Linux 不會當即複製上下文,而是須要時才複製。因此咱們能夠放心效率和內存佔用。不過有一個例外,下文會很快說到。併發

至於 Linux 實現這一過程的原理,下面是個人推測,若是不對,請讀者指出——

現代操做系統依賴於一個很重要的技術,就是內存映射,這須要硬件 CPU 支持 MMU。一個進程看到的內存地址,實際並非 RAM 的實際內存偏移值。操做系統會將進程實際使用的內存地址值映射到實際的硬件 RAM 中。若是系統強行或者意外訪問了映射表中未註冊的地址值區間,那麼硬件 MMU 模塊會發出一個底層硬件中斷。操做系統監聽這個中斷,就能夠知道發生了非法內存訪問,也就是segmentation fault

有了 MMU 這麼強大的東西,怎麼不用呢?fork 以後,對於那些子進程尚未使用到的父進程內存內容,操做系統能夠先放一放,不急着在內存中建立副本。若是子進程處理中出現了越界訪問,那麼操做系統徹底能夠判斷一下該內存是否父進程的內存內容。若是不是,則拋出段錯誤;若是是,就複製內存內容而且建立內存映射——這就是子進程真正複製父進程內容的時刻。

至於程序段?那是隻讀內容,壓根不用拷貝,共享同一段實際內存就好了。

因此就像前文所說,咱們不用擔憂子進程的內存浪費問題。可是例外的狀況就是:好比進程 A fork 了子進程 B,子進程 B 的實際內存使用不多,於是增長的實際內存很少。可是一旦進程 A 退出了,那麼進程 A 所佔有的那些內存不能回收呀,由於操做系統咋知道進程 B 要不要使用 A 的內存內容?

換句話說,調用 fork() 的進程仍是儘可能節省內存,或者說盡可能即用即還。


vfork( )

首先:
vforkfork 最大的區別是:子進程與父進程共享相同的內存空間。換句話說,子進程對全部變量的操做,都會直接影響父進程——而這也就是不少人忌憚 vfork 的緣由。爲了不這樣的操做,vfork 有一個額外的與 fork 的不一樣:

其次:
vfork 以後獲得的子進程,能夠保證在調用 exit 或者 exec 系列調用以前,父進程都不會被執行。這是一個很是重要的特性,上述的兩個特性,也就引出了 vfork 的應用場景。

Shell 調用系統命令

這裏主要是應用了上文提到的第二個特性。詳情請見我之前的文章

跨進程計數

這源於我最近看的某個代碼中的一個功能,那就是對各個進程中某個操做進行計數(也就是++)。

這個時候 vfork 就派上用場了。但要注意,由於上面提到的 vfork 的隱患在,所以這很是考驗程序設計藝術……呃,原本想要多寫一些的,可是實際上,這樣的一個場景,要怎麼設計、怎麼實現,還沒好好思考好好考慮……因此先把思路放在這兒,繼續好好學習吧。

(啥?進程互斥?放心,++ i 是一個原子操做,只須要一條機器指令就能夠完成,不用考慮互斥)

相關文章
相關標籤/搜索