首頁 > 軟體

Linux之fork與vfork區別

2020-06-16 17:37:52

建立一個新進程的方法只有由某個已存在的進程呼叫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

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <sys/types.h> 
  4. #include <unistd.h> 
  5. void test() 
  6.     pid_t pid; 
  7.     pid=vfork(); 
  8.     if(pid<0)  //失敗 
  9.     { 
  10.         printf("vfork errorn"); 
  11.         exit(0); 
  12.     } 
  13.     else if(pid==0) 
  14.     { 
  15.         printf("1:child pid=%d,ppid=%dn",getpid(),getppid()); 
  16.         return//return執行完後,把控制權交給呼叫函數,而exit()執行完後把控制權交給系統 ,改成_exit(0),則會有另一種結果 
  17.     } 
  18.     else 
  19.     { 
  20.         printf("2:parent pid=%d,ppid=%dn",getpid(),getppid()); 
  21.     } 
  22. void fun() 
  23.     int i=0; 
  24.     int buf[100]; 
  25.     for(;i<100;i++) 
  26.     { 
  27.         buf[i]=0; 
  28.     } 
  29.     printf("3:child pid=%d,ppid=%dn",getpid(),getppid()); 
  30. int main() 
  31.     test(); 
  32.     fun(); 
  33.     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流

 


IT145.com E-mail:sddin#qq.com