2021-05-12 14:32:11
Linux之fork與vfork區別
建立一個新進程的方法只有由某個已存在的進程呼叫fork()或vfork()
1.fork()函數
返回值:成功:父進程:返回子進程的PID
子進程:返回0
失敗:父進程返回-1
子進程是父進程的一個拷貝。即子進程從父進程得到資料段和堆、棧段的拷貝,這些需要分配新的記憶體(不是與父進程共用,而是單獨分配記憶體);而對於唯讀的程式碼段,通常使用共用記憶體的方式存取。
fork返回後,子進程和父進程都從呼叫fork函數的下一條語句開始執行。
由於子進程與父進程的執行是無關的,所以,父進程可先於子進程執行,子進程也可以先於父進程執行
eg:
myfork.c
Makefile
執行結果
以前的fork建立一個子進程時,將會建立一個新的地址空間,並且拷貝父進程的資源,然後將會有兩種行為:1.執行從父進程那裡拷貝過來的程式碼段(進程希望複製自身,從而父子進程能同時執行不同段的程式碼);2. 呼叫exec執行一個新的程式碼段(進程想執行另外一個程式)
當進程呼叫exec時,一個進程替換了當前進程的文字、資料、棧、堆段。這樣,前面的拷貝工作就白費力氣了,這種情況下,人們想出了vfork。
vfork並不複製父進程的進程環境,子進程在父進程的地址空間中執行,所以子進程不能進行寫操作,並且兒子“霸佔”著父親的房子的時候,就要委屈父親一下,讓他在外面歇著(阻塞),一旦兒子執行了exec或者exit後,相當於兒子買了屬於自己的房子,這時候就相當於分家了
2.vfork()函數
vfork建立新進程的主要目的在於呼叫exec函數執行另外的一個新程式,在沒呼叫exec或exit之前,子進程的執行是與父進程共用資料段的。
vfork呼叫中,子進程先執行,父進程掛起,直到子進程呼叫exec或者exit,在這以後,父子進程的執行順序不再被限制。
eg:
myvfork.c
Makefile
執行結果
一個經典的例子
test.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <unistd.h>
- void test()
- {
- pid_t pid;
- pid=vfork();
- if(pid<0) //失敗
- {
- printf("vfork errorn");
- exit(0);
- }
- else if(pid==0)
- {
- printf("1:child pid=%d,ppid=%dn",getpid(),getppid());
- return; //return執行完後,把控制權交給呼叫函數,而exit()執行完後把控制權交給系統 ,改成_exit(0),則會有另一種結果
- }
- else
- {
- printf("2:parent pid=%d,ppid=%dn",getpid(),getppid());
- }
- }
- void fun()
- {
- int i=0;
- int buf[100];
- for(;i<100;i++)
- {
- buf[i]=0;
- }
- printf("3:child pid=%d,ppid=%dn",getpid(),getppid());
- }
- int main()
- {
- test();
- fun();
- return 0;
- }
Makefile
執行結果
程式在後續執行時出現了錯誤,並且可知道是在父進程中出現的錯誤
vfork函數呼叫時,子進程比父進程先執行,在呼叫test()函數執行時,子進程執行完之後,將清理test函數的棧空間,然後子進程再呼叫fun()函數,將覆蓋掉test的棧空間,繼續執行fun函數。但是,當子進程退出後,執行父進程,但是,在test函數返回的時候該棧空間已經被子進程破壞了,不存在了,所以就出現了棧錯誤
區別:
1.vfork保證子進程先執行,在它呼叫exec或者exit之後,父進程才可能被排程執行,之後,父子進程的執行順序才不再有限制。如果在呼叫exec或者exit之前,子進程依賴於父進程的進一步動作,則會導致死鎖
2.fork要拷貝父進程的進程環境(資料段),而vfork不需要完全拷貝父進程的進程環境(資料段),在呼叫exec或者exit之前,父子進程共用進程環境(資料段),相當於執行緒概念,此時父進程阻塞等待(因為子進程先執行)。
結束子進程:
exit和_exit函數用於正常終止一個程式,_exit()立即進入核心,exit()則需要先執行一些清除處理(包括呼叫執行各終止處理程式,關閉所有標準I/O流等),然後再進入核心。
結束子進程不用exit(0),而使用_exit(0)。因為_exit(0)在結束進程時,不對標準I/O流進行任何操作,而exit(0)回關閉進程的所有標準I/O流
相關文章