####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()不會複製,直接讓子進程共用父進程的頁表;