循环创建N个子进程
以循環(huán)創(chuàng)建5個進程為例,給出如下代碼,分析其錯誤:
#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(void) {int i;pid_t pid;printf("xxxxxxxxxxx\n");for (i = 0; i < 5; i++){pid = fork( ); if(pid == -1){printf("process of %u creat process failurely!\n",getpid( ));perror("fork");}else if(pid == 0){printf("I'am %dth child , pid = %u\n", i+1, getpid());}else{printf("I'am parent, pid = %u\n",getpid());}}printf("yyyyyyyyyy\n");return 0; }分析:首先在shell中執(zhí)行該文件時,由終端進程fork產(chǎn)生一個子進程來執(zhí)行該程序,然后在for循環(huán)體中,子進程在創(chuàng)建一個個的孫進程。在上述for循環(huán)體中,i=0時,父進程創(chuàng)建了一個子進程,此時父進程與子進程的i都為0(剛fork后兩個的i相等,但是以后不一定相等,它們各自獨立)。此時有兩個進程(父、子進程)都會開始向下執(zhí)行,即后面的代碼都一樣的執(zhí)行,各個進程一直執(zhí)行到return語句后,各個進程才會自動終止(結束)。上述,在for循環(huán)體中創(chuàng)建的子進程,又會在下一次循環(huán)中繼續(xù)去創(chuàng)建子進程,因此最終并不僅僅創(chuàng)建的是5個進程,而是共創(chuàng)建了25-1個進程,總共25個進程。如果循環(huán)n次,則總共為2n個進程。
因此,需要在循環(huán)的過程,保證子進程不再執(zhí)行fork ,因此當(fork() == 0)時,子進程應該立即break;才正確(即跳出循環(huán)體)。
?
練習:通過命令行參數(shù)指定創(chuàng)建進程的個數(shù),如:第1個子進程休眠0秒打印:“我是第1個子進程”;第2個進程休眠1秒打印:“我是第2個子進程”;第3個進程休眠2秒打印:“我是第3個子進程。”
通過該練習掌握框架:循環(huán)創(chuàng)建n個子進程,使用循環(huán)因子i對創(chuàng)建的子進程加以區(qū)分。
//代碼如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(int argc, char *argv[ ]) {if(argc < 2){printf("./a.out 5\n");exit(1);}int i;pid_t pid;printf("xxxxxxxxxxx\n");long int a = strtol(argv[1],NULL,10); //將字符串轉化為10進制整數(shù)for (i = 0; i < a; i++){pid = fork( ); //創(chuàng)建子進程if(pid == -1){printf("process of %u creat process failurely!\n",getpid( )); perror("fork");} //判斷創(chuàng)建進程是否成功,如果當次循環(huán)創(chuàng)建不成功,則不結束該進程,進行下一次循環(huán),再次嘗試創(chuàng)建(這樣等效于少了一次循環(huán))。else if(pid == 0) //如果為子進程,則跳出循環(huán){break;}else //否則(父進程),不執(zhí)行操作,進入下一次循環(huán);}sleep(i); //通過i值來區(qū)分進程,可見父進程睡眠時間最久,為a秒,最先創(chuàng)建的子進程睡眠時間最少,為0秒。if(i < 5)printf("I'am the %dth child process, the ID = %u\n",i+1,getpid( )); //子進程輸出elseprintf("I'am parent process, the ID = %u\n",getpid( )); //父進程輸出return 0; }[root@localhost fork]# ./fork_test 5? //shell終端fork產(chǎn)生子進程來運行這一程序
xxxxxxxxxxx?
I'am the 1th child process, the ID = 16507
I'am the 2th child process, the ID = 16508
I'am the 3th child process, the ID = 16509
I'am the 4th child process, the ID = 16510
I'am the 5th child process, the ID = 16511
I'am parent process, the ID = 16506
[root@localhost fork]#
分析:之所以要引入sleep函數(shù),來使各個進程睡眠,是為了確保父進程最后結束(即最后執(zhí)行return),且越先創(chuàng)建的子進程越先能夠結束。而在創(chuàng)建進程后,每個進程的i值都由自己維護,都要從創(chuàng)建處開始執(zhí)行自己的代碼,從而i值發(fā)生改變。因此就可以用i來區(qū)分是哪一個進程,從而越先創(chuàng)建的進程睡眠時間越少,第i個子進程睡眠時間為i-1秒。下面深度分析sleep函數(shù),代碼如下:
//與上面的代碼相比,只是去掉了一行內(nèi)容: sleep(i);? 因此不再列出,其執(zhí)行結果如下:
[root@localhost fork]# ./fork_test 5
xxxxxxxxxxx
I'am parent process, the ID = 16702
I'am the 3th child process, the ID = 16705
[root@localhost fork]# I'am the 1th child process, the ID = 16703
I'am the 2th child process, the ID = 16704
I'am the 5th child process, the ID = 16707
I'am the 4th child process, the ID = 16706
pwd?? //正常執(zhí)行shell中的pwd命令(前面標簽已經(jīng)輸出)
/mnt/hgfs/share/01_process_test/fork
[root@localhost fork]#
?
分析:由上可以看出,在沒有sleep( )函數(shù)的控制下,每個進程的結束先后順序是隨機的,沒法控制的。在上述程序執(zhí)行過程中,總共參與了7個進程:shell終端進程、父進程(由shell終端fork產(chǎn)生)和5個子進程(由父進程fork產(chǎn)生),這7個進程對CPU的搶占是公平的(隨機的),無法預測。注意一點:父進程只能夠知道其子進程是否結束,而不能直到其孫進程是什么狀態(tài),這7個進程共同使用這一個終端,當shell中執(zhí)行. /fork_test 5時,shell進程會把前臺交給其子進程使用,一旦子進程結束(執(zhí)行了return后),shell進程知道并馬上收回前臺,并輸出[root@localhost fork]# 光標? 等待與用戶再次交互(此時shell進程放棄了CPU,將自己阻塞起來,等待用戶的命令),但是此時那5個子進程并不一定就結束了,因此未結束的進程將會繼續(xù)占用CPU,直到執(zhí)行到return并結束。因此,這些進程的輸出結果會在 [root@localhost fork]#的后面。CPU在1s內(nèi)可執(zhí)行上億條指令,因此睡1s可以絕對保證進程可以按照希望的順序執(zhí)行。
總結
 
                            
                        - 上一篇: fork、getpid、getppid函
- 下一篇: 成都欢乐谷不带身份证可以进去吗
