fork函數 在諸多應用中,創建多個進程是任務分解時行之有效的方法。例如,某一網路伺服器進程可在偵聽客戶端請求的同時,為處理每 請求而創建一新的子進程,與此同時,伺服器進程會繼續偵聽更多的客戶端連接請求。以此類手法分解任務,通常會簡化應用程式的設計,同時提高了系統的併發性。(即,可同時處理更多的任務 ...
fork函數
在諸多應用中,創建多個進程是任務分解時行之有效的方法。例如,某一網路伺服器進程可在偵聽客戶端請求的同時,為處理每---請求而創建一新的子進程,與此同時,伺服器進程會繼續偵聽更多的客戶端連接請求。以此類手法分解任務,通常會簡化應用程式的設計,同時提高了系統的併發性。(即,可同時處理更多的任務或請求。)
1 #include <sys/types.h> 2 #include <unistd.h> 3 pid_t fork(void); //返回:子進程中為0,父進程中為子進程I D,出錯為-1
執行調用後將存在兩個進程,且每個進程都會從fork()的返回處繼續執行。
這兩個進程將執行相同的程式文本段,但卻各自擁有不同的棧段、數據段以及堆段拷貝。子進程的棧、數據以及棧段開始時是對父進程記憶體相應各部分的完全複製。執行fork()之後,每個進程均可修改各自的棧數據、以及堆段中的變數,而並不影響另一進程。
註:現在很多的實現並不做一個父進程數據段和堆的完全拷貝,因為在fork之後經常跟隨著exec。作為替代,使用了在寫時複製( Copy-On-Write, COW)的技術。這些區域由父、子進程共用,而且內核將它們的存取許可權改變為只讀的。(詳見Linux|Unix系統編程手冊)
程式代碼則可通過fork()的返回值來區分父、子進程。在父進程中,fork()將 返回新創建子進程的進程ID。
當無法創建子進程時,fork()將返回-1。 失敗的原因可能在於,進程數量要麼超出了系統針對此真實用戶(realuser ID)在進程數量.上所施加的限制(RLIMIT_ NPROC),要麼是觸及允許該系統創建的最大進程數這一系統級上限。
一般來說,在fork之後是父進程先執行還是子進程先執行是不確定的。這取決於內核所使用的調度演算法。如果要求父、子進程之間相互同步,則要求某種形式的進程間通信。
例子:
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 #include<sys/wait.h> 5 #include<stdlib.h> 6 #include<errno.h> 7 #include<string.h> 8 9 int main() 10 { 11 pid_t childPid; 12 /* if((childPid=fork())==-1) 13 { 14 printf("Fork error %s\n",strerror(errno)); 15 exit(1); 16 } 17 else 18 if(childPid==0) 19 { 20 printf("I am the child : %d\n",getpid()); 21 exit(0); 22 } 23 else 24 { 25 printf("I am the father : %d\n",getpid()); 26 exit(0); 27 } */ 28 switch(childPid=fork()){ 29 case -1: 30 printf("Fork error %s\n",strerror(errno)); 31 exit(1); 32 case 0: 33 printf("I am the child : %d\n",getpid()); 34 exit(0); 35 default: 36 printf("I am the father : %d\n",getpid()); 37 exit(0); 38 39 } 40 return 0; 41 }View Code
結果:
vfork函數
類似於fork(), vfork()可以為調用進程創建一個新的子進程。然而,vfork()是為子進程立即執行exec()的程式而專門設計的。
#include <sys/types.h> #include <unistd.h> pid_t vfork(void); //返回:子進程中為0,父進程中為子進程I D,出錯為-1
vfork()與fork()一樣都創建一個子進程, 但是它並不將父進程的地址空間完全複製到子進程中,因為子進程會立即調用 exec (或exit),於是也就不會存訪該地址空間。不過在子進程調用 exec或exit之前,它在父進程的空間中運行。vfork()和fork()之間的另一個區別是:vfork()保證子進程先運行,在它調用exec或exit之後父進程才可能被調度運行。(如果在調用這兩個函數之前子進程依賴於父進程的進一步動作,則會導致死鎖。)
例子:
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 #include<sys/wait.h> 5 #include<stdlib.h> 6 #include<errno.h> 7 #include<string.h> 8 9 int main() 10 { 11 pid_t childPid; 12 if((childPid=vfork())==-1) 13 { 14 printf("Fork error %s\n",strerror(errno)); 15 exit(1); 16 } 17 else 18 if(childPid==0) //子進程 19 { 20 sleep(1); //子進程睡眠一秒 21 printf("I am the child : %d\n",getpid()); 22 exit(0); 23 } 24 else //父進程 25 { 26 printf("I am the father : %d\n",getpid()); 27 exit(0); 28 } 29 /* switch(childPid=vfork()){ 30 case -1: 31 printf("Fork error %s\n",strerror(errno)); 32 exit(1); 33 case 0: //子進程 34 sleep(1); //子進程睡眠一秒 35 printf("I am the child : %d\n",getpid()); 36 exit(0); 37 default: //父進程 38 printf("I am the father : %d\n",getpid()); 39 exit(0); 40 41 } */ 42 return 0; 43 }vfork_pid.c
結果:
運行程式時可以看到,程式會停一秒然後分別列印出父子進程的ID.也就是說子進程進來就阻塞一秒,但也沒有先去運行父進程,而是讓子進程運行完了之後才運行父進程。
參考資料
Linux/Unix系統編程手冊
Unix環境高級編程
Linux程式設計