fork()與vfork()函式

艾倫lee發表於2019-08-06

fork()與vfork()函式

1 fork()函式

       建立一個和當前程式映像一樣的程式。

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);

       失敗返回-1;成功返回:父程式返回子程式的程式ID(非負);子程式返回0。

       fork會產生一個和父程式完全相同的子程式。Linux中引入了“寫時複製”技術,只有程式空間的各段的內容要發生變化時,才會將程式的內容複製一份給子程式。

       在fork之後exec之前兩個程式用的是相同的物理空間(記憶體區),子程式的程式碼段、資料段、堆疊都是指向父程式的物理空間,也就是說,兩者的虛擬空間不同,但其對應的物理空間是同一個。當父子程式中有更改相應段分配物理空間,如果不是因為exec,核心會給子程式的資料段、堆疊段分配相應的物理空間(至此兩者有各自的程式空間,互不影響),而程式碼段繼續共享父程式的物理空間(兩者的程式碼完全相同)。而如果是因為exec,由於兩者執行的程式碼不同,子程式的程式碼段也會分配單獨的物理空間。

2 父程式fork()後父子程式共享的內容

       fork之後,子程式會拷貝父程式的資料空間、堆和棧空間(實際上是採用寫時複製技術),二者共享程式碼段。所以在子程式中修改全域性變數(區域性變數,分配在堆上的記憶體同樣也是)後,父程式的相同的全域性變數不會改變。

       父子相同之處:全域性變數、.data、.text、棧、堆、環境變數、使用者ID、宿主目錄、程式工作目錄、訊號處理方式….

       父子不同之處:程式ID、fork返回值、父程式ID、程式執行時間、鬧鐘、未決訊號集

       父子程式共享:1.檔案描述符(開啟檔案的結構體)2.mmap建立的對映區。

       不同程式開啟同一個檔案,程式表和檔案表的關係如下圖所示:

 

程式所開啟檔案和在fork後的結構圖如下所示,子程式是共享父程式的檔案表項。

 

3 fork()的原理

       fork()-->clone()-->do_fork()-->copy_process()

       fork()呼叫clone(),clone()呼叫do_fork(),do_fork()呼叫copy_process(),子程式會拷貝父程式的核心棧,thread_info,task_struct,開啟的檔案描述符等。它採用的是copy_on_write機制,即拷貝的只是父程式的虛擬空間但指向同一個實體記憶體。只有在更改段的情況下才會真正的複製實體記憶體。

4 vfork()函式

      

#incldue <sys/types.h>
#include <unistd.h>
pid_t vfork(void);

       vfork()用於建立一個新程式,而新程式的目的是exec一個新程式。vfork()會掛起父程式直到子程式終止或者執行了一個新的可執行檔案的映像。vfork()和fork()一樣都建立一個子程式,但是它並不將父程式的地址空間完全複製到子程式中,因為子程式會立即呼叫exec或exit,於是也就不會存放改地址空間。相反,在子程式呼叫exec或exit之前,它在父程式的空間中執行。如果在呼叫這兩個函式之前子程式依賴於父程式的進一步動作,則會導致死鎖。

5 vfork()和fork()之間的區別:

  1. vfork()保證子程式先執行,在呼叫exec或者exit之前與父程式資料是共享的,在它呼叫exec或exit之後父程式才可能被排程執行;fork()建立子程式後會複製父程式的虛擬空間,但實體記憶體都是指向同一個,它是兩個同時執行的。
  2. 如果在呼叫exec或exit之前子程式依賴於父程式的進一步動作,則會導致死鎖。
  3. fork()的父子程式執行次序不確定;vfork()保證子程式先執行,在呼叫exec或者exit之前與父程式資料是共享的,在它呼叫exec或exit之後父程式才可能被排程執行。

相關文章