在Unix/Linux中用fork函數創建一個新的進程。進程是由當前已有進程調用fork函數創建,分叉的進程叫子進程,創建者叫父進程。該函數的特點是調用一次,返回兩次,一次是在父進程,一次是在子進程。兩次返回的區別是子進程的返回值為0,父進程的返回值是新子進程的ID。子進程與父進程繼續並發運行。如果父進程繼續創建更多的子進程,子進程之間是兄弟關系,同樣子進程也可以創建自己的子進程,這樣可以建立起定義關系的進程之間的一種層次關系。
程序包含位於內存的多個組成部分,執行程序的過程將根據需要來訪問這些內容,包括文本段(text segment)、數據段(data segments)、棧(stack)和堆(heap)。文本段中存放CPU所執行的命令,數據段存放進程操作的所有數據變量,棧存放自動變量和函數數據,堆存放動態內存分配情況數據。當進程被創建時,子進程收到父進程的數據副本,包括數據空間、堆、棧和進程描述符。
程序1:創建一個子進程,子進程對繼承的數據進行修改,然后分別輸出父子進程的信息。程序如下:
View Code
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <errno.h> 6 #include <sys/types.h> 7 8 int add(int a,int b); 9 //全局變量 10 int global = 99; 11 char buf[] = "Input a string: "; 12 13 int main() 14 { 15 pid_t pid; 16 int val,ret; 17 char *str; 18 val =49; 19 str = (char*)malloc(100*sizeof(char)); 20 memset(str,0,100*sizeof(char)); 21 if((pid = fork()) == -1) 22 { 23 perror("fork() error"); 24 exit(-1); 25 } 26 if(pid == 0) //子進程 27 { 28 printf("Child process start exec.\n"); 29 global++; 30 val++; 31 } 32 if(pid >0) //父進程 33 { 34 sleep(10); //等待子進程執行 35 printf("Parent process start exec.\n"); 36 } 37 printf("pid=%d,ppid=%d,global=%d,val=%d\n",getpid(),getppid(),global,val); 38 write(STDOUT_FILENO,buf,strlen(buf)); 39 read(STDIN_FILENO,str,100); 40 write(STDOUT_FILENO,str,strlen(str)); 41 ret = add(global,val); 42 printf("global+val=%d\n",ret); 43 exit(0); 44 } 45 46 int add(int a,int b) 47 { 48 return (a+b); 49 }
fork函數執行后程序結構圖如下:

子進程與父進程並行執行,因此在父進程中sleep(10),讓子進程先執行,然后再執行父進程。
程序執行結果如下所示:

如何創建多個子進程呢?在開發並發服務器時,用到的進程池模型需要先創建指定書目的子進程。舉個例子,假如我們現在需要創建2個子進程,很容易想到的是調用一個循環,執行fork函數2次即可。嘗試一下是否可行呢?代碼如下:
View Code
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <errno.h> 6 #include <sys/types.h> 7 8 int main() 9 { 10 int i; 11 pid_t pid; 12 printf("pid=%d , ppid=%d\n",getpid(),getppid()); 13 //通過一個循環創建對個子進程 14 for(i=0;i<2;++i) 15 { 16 pid = fork(); 17 if(pid == 0) 18 { 19 printf("create child process successfully.\n"); 20 printf("pid=%d , ppid=%d\n",getpid(),getppid()); 21 printf("i=%d\n",i); 22 } 23 else if(pid== -1) 24 { 25 perror("fork() error"); 26 exit(-1); 27 } 28 else 29 { 30 sleep(3); 31 printf("parent process.\n"); 32 printf("pid=%d , ppid=%d\n",getpid(),getppid()); 33 printf("i=%d\n",i); 34 } 35 } 36 37 exit(0); 38 }
程序執行結果如下:

從結果來看,子進程的數目不是2而是3,這是為什么呢?先簡單的分析一下:從結果看出父進程ID為10669,子進程的ID分別為:10670、10671、10672。
父子進程之間的關系如下:

ID為10670的子進程也調用fork函數,創建了一個進程。因為fork函數創建的進程是父進程的一份拷貝,保存了當前的數據空間、堆、棧及共享代碼區域。正確的方式應該是在子進程中跳出,停止繼續fork。改進的代碼如下:
View Code
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <errno.h> 6 #include <sys/types.h> 7 8 int main() 9 { 10 int i; 11 pid_t pid; 12 printf("pid=%d , ppid=%d\n",getpid(),getppid()); 13 for(i=0;i<2;++i) 14 { 15 pid = fork(); 16 if(pid == 0) 17 { 18 printf("create child process successfully.\n"); 19 printf("pid=%d , ppid=%d\n",getpid(),getppid()); 20 printf("i=%d\n",i); 21 //子進程跳出循環,防止子進程繼續創建子進程 22 break; 23 } 24 else if(pid== -1) 25 { 26 perror("fork() error"); 27 exit(-1); 28 } 29 else 30 { 31 sleep(3); 32 printf("parent process.\n"); 33 printf("pid=%d , ppid=%d\n",getpid(),getppid()); 34 printf("i=%d\n",i); 35 //父進程繼續創建子進程 36 continue; 37 } 38 } 39 40 exit(0); 41 }
程序執行結果如下:

從結果可以看出這父進程(ID為10789)創建了兩個子進程(ID分別為:10790、10791)。
現有有這樣一個面試題,程序如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 6 int main() 7 { 8 pid_t pid1; 9 pid_t pid2; 10 11 pid1 = fork(); 12 pid2 = fork(); 13 14 printf("pid1=%d,pid2=%d\n",pid1,pid2); 15 exit(0); 16 }
要求如下:
已知從這個程序執行到這個程序的所有進程結束這個時間段內,沒有其它新進程執行。
1、請說出執行這個程序后,將一共運行幾個進程。
2、如果其中一個進程的輸出結果是“pid1:1001, pid2:1002”,寫出其他進程的輸出結果(不考慮進程執行順序)。
這個題目考查fork函數的理解。fork的作用是復制一個與當前進程一樣的進程。新進程的所有數據(變量、環境變量、程序計數器等)數值都和原進程一致,但是是一個全新的進程,並作為原進程的子進程,父子進程並行的執行剩下的部分。
程序的執行過程如下:
(1)程序開始執行時候系統分配一個進程進行執行,稱該進程為主進程P,進程ID題目未給,
(2)主進程執行到第一個fork函數的時候,創建一個新的子進程P1,有題目可知進程ID為1001,fork函數有兩個返回值,返回pid=0代表子進程P1,pid1>0代表父進程P。
(3)現在有兩個進程P和P1,分別執行剩下部分,
(4)P進程(父進程,所以pid1=1001)調用fork創建子進程P2,返回兩個值中pid2=1002表示P2的進程ID返回給父進程P,pid2=0子進程P2本身,所以輸出pid1=1001, pid2=1002和pid1=1001,pid2=0。
(5)P1進程(子進程,所以pid1=0)調用fork創建子進程P3,進程ID類推為1003,返回兩個值中pid2=1003表示P3的進程ID返回給父進程P1,pid2=0標識進程P3本身。所以輸出pid1=0,pid2=1003和pid1=0,pid2=0。
(6)執行整個結束。
根據以上分析可知答案:
1、一共執行了四個進程。(P0, P1, P2, P3)
2、另外幾個進程的輸出分別為:
pid1:1001, pid2:0
pid1:0, pid2:1003
pid1:0, pid2:0
上機測試如下:

測試結果如下:

測試結果雖然不是1001,但是可以看出理論分析過程是正確的。
題目來自:http://www.cnblogs.com/leoo2sk/archive/2009/12/11/talk-about-fork-in-linux.html
