介紹
vfork和fork一樣都可以用來創建一個新進程,與fork相比,它有一些自己獨特的用處。下面是他們的一些異同
* vfork 與fork一樣都是調用一次,返回兩次(一個是父進程調用vfork後的返回值,該返回值爲子進程的pid。一個是子進程調用vfork的返回值,該返回值爲0)
* 使用fork創建一個子進程的時候,子進程只是完整複製子進程的資源。這樣得到的子進程獨立於父進程,具有良好的併發性。而使用vfork創建子進程時,操作系統並不將父進程的地址空間完全複製到子進程,用vfork創建的子進程共享父進程的地址空間,也就是說子進程完全運行在父進程的地址空間上。子進程對該地址空間中任何數據的修改同樣爲父進程所見。
* 使用fork創建一個子進程時,哪個進程先運行取決於系統的調度算法。而vfork一個進程時,vfork保證子進程先運行,當它調用exec或exit後,父進程纔可以被調度運行。如果在調用exec或exit之前子進程要依賴父進程的某幾個行爲,就會導致死鎖。
fork創建一個子進程時,子進程要將父進程幾乎每種資源都複製,所以fork是一個開銷很大的系統調用,這些開銷並不是所有情況都適用。比如fork一個進程後,立即調用exec執行另一個應用程序,那麼fork過程中子進程對父進程地址空間的複製將是一個多餘的過程。vfork不會拷貝父進程的地址空間,大大減少了系統開銷。
我們看看下面這段程序。
代碼
fork
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int qvar=5;
int main()
{
pid_t pid;
int var=1,i;
char str[]="I'm here";
printf("fork is different with vfork\n");
pid=fork();
//pid=vfork();
switch(pid)
{
case 0:
i=3;
while(i--)
{
printf("Child process is running\n");
qvar++;
var++;
sleep(1);
}
printf("Child's qvar = %d,var = %d,str = %s\n",qvar,var,str);
exit(0);
break;
case -1:
perror("failed\n");
exit(0);
break;
default:
i=5;
while(i-->0)
{
printf("Parent is running\n");
qvar++;
var++;
sleep(1);
}
printf("Parent's qvar = %d,var = %d,str = %s\n",qvar,var);
exit(0);
}
}
fork結果
分析
可見用fork創建子進程時,子進程繼承了父進程的全劇變量和局部變量。而且根據子進程的qvar,var均增加了3。父進程的qvar,var也增加了3.且兩者的值並未影響可知父子進程有各自獨立的地址空間。
我們將上面vfork的註釋去掉,然後再註釋調fork。
vfork結果
分析
首先可見子進程要先於父進程運行,並且由qvar,var的值遞增了8可見,vfork後子進程和父進程共享了一段地址空間。
注意
如果我們將vfork下子進程的exit(0)去掉,那麼我們看看會發生什麼情況
我們可以看到父進程的var成了隨機值,str也找不到了。
這是因爲vfork是這樣的工作的:
(1)、保證子進程先執行。
(2)、當子進程調用exit()或exec()後,父進程往下執行。
當去掉子子進程下的exit後,程序默認return。而return會將棧釋放,因此當子進程執行完,回到父進程時,父進程的棧已被子進程釋放掉了(這就是赤裸裸的坑爹!),因此var爲隨機值。
因此當我們使用vfork時一定記得在子進程結束後用exit結束,不然會出現一些意想不到的結果。關於這點可以參考
用return和exit結束fork和vfork創建的子進程的思考