fork()函數功能——建立新進程html
一、父子進程有獨立的數據段、堆、棧,共享代碼段linux
Linux中每一個進程都有4G的虛擬地址空間(獨立的3G用戶空間和共享的1G內核空間),fork()建立的子進程也不例外。子進程資源的由來:函數
一、1G內核空間既然是全部進程共享,所以fork()建立的子進程天然也將擁有;spa
二、3G的用戶空間是從父進程進程而來。操作系統
fork()建立子進程時繼承了父進程的數據段、代碼段、棧段、堆,注意從父進程繼承來的是虛擬地址空間,同時也複製了頁表(沒有複製物理塊)。所以,此時父子進程擁有相同的虛擬地址,映射的物理內存也是一致的(獨立的虛擬地址空間,共享父進程的物理內存)。.net
因爲父進程和子進程共享物理頁面,內核將其標記爲「只讀」(相似mmap)的private的方式),父子雙方均沒法對其修改。不管父進程和子進程什麼時候試圖對一個共享的頁面執行寫操做,就產生一個錯誤,這時內核就把這個頁複製到一個新的頁面給這個進程,並標記爲可寫,同時修改頁表,把原來的只讀頁面標記爲「可寫」,留給另一個進程使用——寫時複製技術。htm
注意:內核在爲子進程分配物理內存時,並無將代碼段對應的數據另外複製一份給子進程,最終父子進程代碼段映射的是同一塊物理內存(代碼段在單個進程內部原本就是隻讀的)。 blog
每一個進程的虛擬地址空間均可以是0到4G,只不過其中只有一部分有權訪問,每一個進程能夠有不一樣的映射。兩次運行同一個程序就是使用的相同的虛擬地址,可是映射到的物理地倒是不同的。每一個進程都有本身的虛擬地址空間,不一樣進程的相同的虛擬地址顯然能夠對應不一樣的物理地址。所以地址相同(虛擬地址)而值不一樣沒什麼奇怪。繼承
寫時複製技術參見:http://www.cnblogs.com/wuchanming/p/4495479.html隊列
二、一次調用兩次執行
參見:http://blog.csdn.net/shenwansangz/article/details/39184789
三、 競爭條件
Linux是一個多用戶操做系統,在同一時間會有許多的用戶在爭奪系統的資源.有時進程爲了早一點完成任務就建立子進程來爭奪資源. 一旦子進程被建立,父子進程一塊兒從fork處繼續執行,相互競爭系統的資源.有時候咱們但願子進程繼續執行,而父進程阻塞,直到子進程完成任務.這個時候咱們能夠調用wait或者waitpid系統調用.
對子進程來講,fork返回給它0,但它的pid絕對不會是0;之因此fork返回0給它,是由於它隨時能夠調用getpid()來獲取本身的pid;fork以後父子進程除非採用了同步手段,不然不能肯定誰先運行,也不能肯定誰先結束。認爲子進程結束後父進程才從fork返回的,這是不對的,fork不是這樣的,vfork才這樣。
四、fork以後跟隨exec
fork()會產生一個和父進程徹底相同的子進程,但子進程在此後多會exec系統調用,出於效率考慮,linux中引入了「寫時複製「技術,也就是隻有進程空間的各段的內容要發生變化時,纔會將父進程的內容複製一份給子進程。因爲寫時複製,在fork以後exec以前兩個進程有獨立的虛擬地址空間,共享物理內存。只有其中一方須要寫操做時,再爲子進程的數據段、棧、堆分配物理空間。但在子進程上調用exec時,會清空棧、堆,以及和父進程共享的空間,從新加載新的代碼段,這樣避免了「寫時複製」拷貝共享頁面的機會,父進程也同時獨自擁有了原來共享的物理內存(可對其讀寫操做)。
fork出來子進程以後,父子進程哪一個先調度直接決定了是否須要拷貝的問題?內核通常會先調度子進程,由於不少狀況下子進程是要立刻執行exec,而避免無用的複製。若是父進程先調度極可能寫共享頁面,會產生「寫時複製」的無用功。因此,通常是子進程先調度滴。
在網上看到還有個細節問題就是,fork以後內核會經過將子進程放在隊列的前面,以讓子進程先執行,以避免父進程執行致使寫時複製,然後子進程執行exec系統調用,因無心義的複製而形成效率的降低。
若是不是由於exec,子進程執行時極可能修改內存,內核會給子進程的數據段、堆棧段分配相應的物理空間(至此二者有各自的進程空間,互不影響),而代碼段繼續共享父進程的物理空間(二者的代碼徹底相同)。而若是是由於exec,因爲二者執行的代碼不一樣,子進程的代碼段也會分配單獨的物理空間。
五、fork()函數的實現過程