Linux-vfork與fork簡單對比分析

####fork相關問題:
#####一、fork基礎瞭解
#####fork作用爲創建一個子進程,在使用了fork命令後,內核會分配新的內存塊和數據結構給子進程,並且將父進程的部分數據結構內容拷貝到子進程,最後再將子進程添加到系統進程列表中,添加完成後fork返回,開始調度。

頭文件:#include < unistd.h >
函數原型:pid_t fork( )
**返回值:**返回值大於0則當前進程爲父進程,等於0代表爲子進程,小於零代表創建子進程失敗。

#####通過一個例子來了解:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4     
  5 int main()
  6 {
  7     int tmp = 5;
  8     pid_t res = fork();
  9     if(res < 0){
 10         //fork失敗
 11         perror("fork");
 12     }else if(res == 0){
 13         //該進程爲子進程
 14         printf("im child[%d],fasther is %d,tmp is %d.\n",getpid(),getppid(),tmp++);
 15     }else{
 16         //該進程爲父進程
 17         printf("im father[%d],tmp is %d.\n",getpid(),tmp++);
 18     }
 19     printf("tmp = %d\n",tmp);
 20     return 0;
 21 }            

#####運行結果:
im father[3128],tmp is 5.
tmp = 6
im child[3129],fasther is 1,tmp is 5.
tmp = 6
#####相關問題小結:
#####通過結果很明顯的能看出本次調用中,先執行父進程,對應pid爲3128,在父進程中tmp++,所以輸出爲6;關鍵問題在於子進程,有兩個關鍵點。
#####**①爲什麼結果中子進程父親pid爲1:**通過輸出我們能看出父進程先執行完成後才執行的子進程,也就是說當子進程執行時父進程已結束,此時該子進程相當於一個孤兒進程,被pid爲1也就是Init進程所管理,所以子進程的ppid爲1;
#####②爲什麼子進程最後輸出tmp值還爲6: fork進程採用的是寫時拷貝,父子進程一開始共享一片內存區域,但是隻有有一方要對數據進行修改,則再開闢一塊空間,防止相互修改影響。所以在上述代碼中,雖說是一個tmp,其實內存中各自保留了一份值。


#####二、關於fork過程中寫時拷貝:
這裏寫圖片描述
這裏寫圖片描述
#####這下就不難看出,父子進程數據段和代碼段開始時是共享一塊對應的內存,當一方嘗試寫入時,便產生了寫時拷貝。**需要注意的是:fork之前,父進程獨立執行,fork之後,父子兩個執行流分別執行,至於誰先執行,由調度器決定。**可通過下面例子很明顯的看出是從fork之後才分別執行。


  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 
  5 int main()
  6 {
  7     int tmp = 5;
  8     printf("There is fork before\n");
  9     pid_t res = fork();
 10     if(res < 0){
 11         //fork失敗
 12         perror("fork");
 13     }else if(res == 0){
 14         //該進程爲子進程
 15         printf("im child[%d],tmp is %d.\n",getpid(),tmp++);
 16     }else{
 17         //該進程爲父進程
 18         printf("im father[%d],tmp is %d.\n",getpid(),tmp++);
 19     }
 20     printf("tmp = %d\n",tmp);
 21     return 0;
 22 }
~      

#####輸出結果:
#####There is fork before
#####im father[3625],tmp is 5.
#####tmp = 6
#####im child[3626],tmp is 5.
#####tmp = 6


#####三、fork調用失敗的原因:
#####①系統中已經存在太多進程,無法再創建新的進程。可通過ulimit -a命令查看當前所有的資源限制。
#####②內存不足,由於開闢每個新的進程都要分配一個PCB,併爲新進程分配資源,內存都不足也就別提還想着再創建進程了。


####vfork相關問題:
#####一、vfork基礎瞭解
#####<1>vfork創建新進程的主要目的在於用exec函數執行另外的程序,實際上,在沒調用exec或_exit之前**子進程與父進程共享數據段。**在vfork調用中,**子進程先運行,父進程掛起,**直到子進程調用exec或_exit,在這以後,父子進程的執行順序不再有限制。

頭文件:#include < unistd.h >
函數原型:pid_t vfork( )
**返回值:**返回值大於0則當前進程爲父進程,等於0代表爲子進程,小於零代表創建子進程失敗。


#####通過一個例子來了解:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int tmp = 3;
  5 
  6 int main()
  7 {
  8     pid_t res = vfork();
  9     if(res < 0){
 10         perror("vfork");
 11         _exit(1);
 12     }else if(res == 0){
 13         tmp = 10;
 14         printf("child res = %d\n",tmp);
 15         _exit(0);
 16     }else{
 17         printf("father res = %d\n",tmp);
 18     }
 19 
 20     return 0;
 21 }

#####輸出結果:
#####child res = 10
#####father res = 10
#####結果分析:正如上面所說的,子進程直接公用父進程的頁表,改變子進程的數據也會影響到父進程。
這裏寫圖片描述


#####<2>vfork用處:
#####vfork()跟fork()類似,都是創建一個子進程,這兩個函數的的返回值也具有相同的含義。但是vfork()創建的子進程基本上只能做一件事,那就是立即調用_exit()函數或者exec函數族成員,調用任何其它函數(包括exit())、修改任何數據(除了保存vfork()返回值的那個變量)、執行任何其它語句(包括return)都是不應該的。更需要注意的是:調用vfork()之後,父進程會一直阻塞,直到子進程調用_exit()終止,或者調用exec函數族成員。


#####<3>爲什麼只能用_exit退出:
#####exit()是對_exit()的封裝,它自己在調用_exit()前會做很多清理工作,其中包括刷新並關閉當前進程使用的流緩衝(比如stdio.h裏面的printf等),由於vfork()的子進程完全共享了父進程地址空間,子進程裏面的流也是共享的父進程的流,所以子進程裏面是不能做這些事的。直接return就更不行了,子進程return以後,會從當前函數的外部調用點後面繼續執行,這後面子進程可能將會執行很多語句,結果就沒法預料了。在man手冊中也強調了這一點,必須使用_exit退出。


####總結對比:
#####一、父子進程調用先後順序不同:fork()由調度決定,vfork先子進程,後父進程;
#####二、對父進程頁表的操作不同:fork()會複製父進程的頁表,而vfork()不會複製,直接讓子進程共用父進程的頁表;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章