UNIX操作系統中有兩種創建新進程的機制,分別是 fork 和 exec
(1) fork 可以創建當前進程的一個副本,父進程和子進程只有PID(進程ID)不同。在該系統調
用執行之後,系統中有兩個進程,都執行同樣的操作。父進程內存的內容將被複制,至少從程序的角
度來看是這樣。 Linux使用了一種衆所周知的技術來使 fork 操作更高效,該技術稱爲寫時複製 (copy on
write),主要的原理是將內存複製操作延遲到父進程或子進程向某內存頁面寫入數據之前,在只讀訪
問的情況下父進程和子進程可以共用同一內存頁。
例如,使用 fork 的一種可能的情況是,用戶打開另一個瀏覽器窗口。如果選中了對應的選項,瀏
覽器將執行 fork ,複製其代碼,接下來子進程中將啓動適當的操作建立新窗口。
fork 生成當前進程的一個相同副本,該副本稱之爲子進程。原進程的所有資源都以適當的方
式複製到子進程,因此該系統調用之後,原來的進程就有了兩個獨立的實例。這兩個實例的
聯繫包括:同一組打開文件、同樣的工作目錄、內存中同樣的數據(兩個進程各有一份副本),
等等。此外二者別無關聯。
(2) exec 將一個新程序加載到當前進程的內存中並執行。舊程序的內存頁將刷出,其內容將替換
爲新的數據。然後開始執行新程序。
exec 從一個可執行的二進制文件加載另一個應用程序,來代替當前運行的進程。換句話說,
加載了一個新程序。因爲 exec 並不創建新進程,所以必須首先使用 fork 複製一箇舊的程序,
然後調用 exec 在系統上創建另一個應用程序。
創建進程示例代碼:
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid;
pid = fork();
if (pid > 0){
cout << "This is the parent: " << pid << endl;
}
else if (pid == 0){
cout << "This is the child: " << pid << endl;
}
else{
cout << "fail to fork" << endl;
}
return 0;
}
運行結果:
This is the parent: 15256
This is the child: 0
其實這裏有一個問題,爲什麼子進程不會繼續執行fork命令?
我猜是因爲父進程執行了fork語句之後,子進程和父進程此時並沒有明顯的區別,所以此時兩個進程共用一段內存,子進程直接接着父進程運行到的語句開始運行,並不會在此運行fork語句。
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid;
for (int i = 0; i < 2; i++){
pid = fork();
if (pid > 0){
cout << "This is the parent: " << pid << endl;
}
else if (pid == 0){
cout << "This is the child: " << pid << endl;
}
else{
cout << "fail to fork" << endl;
}
}
return 0;
}
運行結果:
This is the parent: 16389
This is the child: 0
This is the parent: 16390
This is the child: 0
This is the parent: 16391
This is the child: 0
可以分解爲如下幾步:
init i = 0 i = 1
father ---> father ---> father
| |-> son
|-> son ---> father
|-> son
初始化的時候,只有一個父進程,第一次執行fork語句後,創建一個子進程,子進程跟父進程的數據保持一致,然後在第二次執行fork語句時,父進程又創建出一個子進程,接着父進程第一次創建的子進程現在也運行到了fork命令,並創建出一個子進程,等於說子進程有創建出了子進程,所以現在一共有四個進程,一個父進程,三個子進程,其中三個子進程中有一個是父進程,但這個父進程又是最初的父進程的子進程。