一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

linux 進(jìn)程(二)

 風(fēng)之library 2014-12-31
一、進(jìn)程的創(chuàng)建fork()函數(shù)



 由fork創(chuàng)建的新進(jìn)程被稱為子進(jìn)程(child process)。該函數(shù)被調(diào)用一次,但返回兩次。兩次返回的區(qū)別是子進(jìn)程的返回值是0,而父進(jìn)程的返回值則是 新子進(jìn)程的進(jìn)程ID。將子進(jìn)程ID返回給父進(jìn)程的理由是:因?yàn)橐粋€(gè)進(jìn)程的子進(jìn)程可以多于一個(gè),所有沒(méi)有一個(gè)函數(shù)使一個(gè)進(jìn)程可以獲得其所有子進(jìn)程的進(jìn)程ID。fork使子進(jìn)程得到返回值0的理由是:一個(gè)進(jìn)程只會(huì)有一個(gè)父進(jìn)程,所以子進(jìn)程總是可以調(diào)用getppid以獲得其父進(jìn)程的進(jìn)程ID(進(jìn)程 ID  0總是由交換進(jìn)程使用,所以一個(gè)子進(jìn)程的進(jìn)程ID不可能為0)。

    子進(jìn)程和父進(jìn)程繼續(xù)執(zhí)行fork之后的指令。子進(jìn)程是父進(jìn)程的復(fù)制品。例如,子進(jìn)程獲得父進(jìn)程數(shù)據(jù)空間、堆和棧的復(fù)制品。注意,這是子進(jìn)程擁有的拷貝。父、子進(jìn)程并共享這些存儲(chǔ)部分。如果正文段是只讀的,則父、子進(jìn)程共享正文段。
    現(xiàn)在很多的實(shí)現(xiàn)并不做一個(gè)父進(jìn)程數(shù)據(jù)段和堆的完全拷貝,因?yàn)樵趂ork之后經(jīng)常跟隨著exec。作為替代,使用了寫時(shí)復(fù)制(copy-on-write,cow)的技術(shù)。這些區(qū)域由父、子進(jìn)程共享,而且內(nèi)核將他們的存取許可權(quán)改變位只讀的。如果有進(jìn)程試圖修改這些區(qū)域,則內(nèi)核包異常,典型的是虛存系統(tǒng)中的“頁(yè)”,做一個(gè)拷貝。

實(shí)例1:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int glob = 6;
char buf[] = "a write to stdout\n";

int main()
{
int var;
int pid;

var = 88;

if(write(STDOUT_FILENO,buf,sizeof(buf) -1) != sizeof(buf) -1)
{
perror("fail to write");
return -1;
}

printf("before fork\n");

if((pid = fork()) < 0)
{
perror("fail to fork");
return -1;
}else 
if(pid == 0)
{
glob ++;
var ++;
}else{
sleep(2);
}

printf("pid = %d,glob = %d,var = %d\n",getpid(),glob,var);
exit(0);
}

運(yùn)行結(jié)果:




從上面可以看出,因?yàn)樽舆M(jìn)程和父進(jìn)程擁有獨(dú)立的物理內(nèi)存空間,所以當(dāng)子進(jìn)程對(duì)拷貝來(lái)的數(shù)據(jù)做修改的時(shí)候,并沒(méi)有影響到父進(jìn)程。

注意:
        1.一般來(lái)說(shuō),fork之后父進(jìn)程先執(zhí)行還是子進(jìn)程先執(zhí)行是不確定的。這取決于內(nèi)核所使用的調(diào)度算法。
        2.從上面可以看到兩次的運(yùn)行結(jié)果不一樣。我們知道write函數(shù)是不帶緩存的。因?yàn)樵趂ork之前調(diào)用write,所以其數(shù)據(jù)寫到標(biāo)準(zhǔn)輸出一次。但是,標(biāo)準(zhǔn) I/O庫(kù)是帶緩存的。如果標(biāo)準(zhǔn)輸出連到終端設(shè)備,則它是行緩存的,否則它是全緩存的。當(dāng)以交互方式運(yùn)行該程序時(shí),只得到printf輸出的行一次,其原因是標(biāo)準(zhǔn)輸出緩存由新行符刷新。但是當(dāng)將標(biāo)準(zhǔn)輸出重新定向到一個(gè)文件時(shí),卻得到printf輸出行兩次。其原因是,在fork之前調(diào)用了printf一次,當(dāng)調(diào)用fork時(shí),該行數(shù)據(jù)仍在緩存中,然后在父進(jìn)程數(shù)據(jù)空間復(fù)制到子進(jìn)程中時(shí),該緩存數(shù)據(jù)也被復(fù)制到子進(jìn)程中。于是那時(shí)父、子進(jìn)程各自有了帶該行內(nèi)容的緩存。在exit之前的第二個(gè)printf將其數(shù)據(jù)添加到現(xiàn)存的緩存中。當(dāng)每個(gè)進(jìn)程終止時(shí),其緩存中的內(nèi)容被寫到相應(yīng)文件中。


實(shí)例 2:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int glob = 6;

int main()
{
int var;
int pid;

var = 88;

printf("father:\n");
printf("&glob = %p\n",&glob);
printf("&var = %p\n",&var);
printf("__________________________________\n");

if((pid = fork()) < 0)
{
perror("fail to fork");
return -1;

}else 
if(pid == 0)
{
printf("child var value not change\n:");
printf("&glob = %p\n",&glob);
printf("&var = %p\n",&var);
glob ++;
var ++;

printf("__________________________________\n");
printf("child var value change:\n");
printf("&glob = %p\n",&glob);
printf("&var = %p\n",&var);
}

exit(0);
}

運(yùn)行結(jié)果如下:



   從上面可以看出,根據(jù)copy-on-write的思想,在子進(jìn)程中,改變父進(jìn)程的數(shù)據(jù)時(shí),會(huì)先 復(fù)制父進(jìn)程的數(shù)據(jù)修然后再改,從而達(dá)到子進(jìn)程對(duì)數(shù)據(jù)的修改不影響父進(jìn)程。但是我們發(fā)現(xiàn),復(fù)制的前后,其值的地址都是一樣的。為什么呢?子進(jìn)程拷貝的時(shí)候也拷貝了父進(jìn)程的虛擬內(nèi)存"頁(yè)",這樣他們的虛擬地址都一樣,但是對(duì)應(yīng)不同的物理內(nèi)存空間。

二、copy-on-write工作原理

    假設(shè)進(jìn)程A創(chuàng)建子進(jìn)程B,之后進(jìn)程A和進(jìn)程B共享A的地址空間,同時(shí)該地址空間中的頁(yè)面全部被標(biāo)識(shí)為寫保護(hù)。此時(shí)B若寫address的頁(yè)面,由于寫保護(hù)的原因會(huì)引起寫異常,在異常處理中,內(nèi)核將address所在的那個(gè)寫保護(hù)頁(yè)面復(fù)制為新的頁(yè)面,讓B的address頁(yè)表項(xiàng)指向該新的頁(yè)面,新頁(yè)面可寫。而A的address頁(yè)表項(xiàng)依然指向那個(gè)寫保護(hù)的頁(yè)面。然后當(dāng)B在訪問(wèn)address時(shí)就會(huì)直接訪問(wèn)新的頁(yè)面了,不會(huì)在訪問(wèn)到哪個(gè)寫保護(hù)的頁(yè)面。當(dāng)A試圖寫address所在的頁(yè)面時(shí),由于寫保護(hù)的原因此時(shí)也會(huì)引起異常,在異常處理中,內(nèi)核如果發(fā)現(xiàn)該頁(yè)面只有一個(gè)擁有進(jìn)程,此種情況下也就是A,則直接對(duì)該頁(yè)面取消寫保護(hù),此后當(dāng)A再訪問(wèn)address時(shí)不會(huì)在有寫保護(hù)錯(cuò)誤了。如果此時(shí)A又創(chuàng)建子進(jìn)程C,則該address所在的頁(yè)面又被設(shè)置為寫保護(hù),擁有進(jìn)程A和C,同時(shí)其他頁(yè)面例如PAGEX依然維持寫保護(hù),只是擁有進(jìn)程A、B和C。如果此時(shí)A訪問(wèn)PAGEX,則異常處理會(huì)創(chuàng)建一個(gè)新頁(yè)面并將PAGEX中的內(nèi)容復(fù)制到該頁(yè)面,同時(shí)A相應(yīng) 的pte指向該新頁(yè)面。如果此時(shí)C也訪問(wèn)PAGEX,也會(huì)復(fù)制新頁(yè)面并且讓C對(duì)應(yīng)的pte指向新頁(yè)面。如果B再訪問(wèn)PAGEX,則由于此時(shí)PAGEX只有一個(gè)擁有進(jìn)程B,故不再?gòu)?fù)制新頁(yè)面,而是直接取消該頁(yè)面的寫保護(hù),由于B的pte本來(lái)就是直接指向該頁(yè)面,所以無(wú)需要在做其它工作。

三、exit和_exit

(1)正常終止:
    (a)在main函數(shù)內(nèi)執(zhí)行return語(yǔ)句。這等效于調(diào)用exit。
    (b)調(diào)用exit函數(shù)
    (c)調(diào)用_exit系統(tǒng)調(diào)用函數(shù)

(2)異常終止:
    (a)調(diào)用abort。它產(chǎn)生SIGABRT信號(hào),所以是一種異常終止的一種特列。
    (b)當(dāng)進(jìn)程接收到某個(gè)信號(hào)時(shí)。例如,進(jìn)程越出其地址空間訪問(wèn)存儲(chǔ)單元,或者除以0,內(nèi)核就會(huì)為該進(jìn)程產(chǎn)生相應(yīng)的信號(hào)。

注意:不管進(jìn)程如何終止,最后都會(huì)執(zhí)行內(nèi)核中的同一段代碼。這段代碼為相應(yīng)進(jìn)程關(guān)閉所有打開描述符,釋放它所使用的存儲(chǔ)器等。


exit和_exit的不同



_exit()函數(shù)的作用最為簡(jiǎn)單:直接進(jìn)程停止運(yùn)行,清除其使用的內(nèi)存空間,并銷毀其在內(nèi)核中的各種數(shù)據(jù)結(jié)構(gòu);

exit()函數(shù)與_exit()函數(shù)最大的區(qū)別就在于exit()函數(shù)在調(diào)用exit系統(tǒng)調(diào)用之前要檢查文件的打開情況,把文件緩沖區(qū)中的內(nèi)容寫回文件,就是"清理I/O"緩沖。

探究 1._exit()

//_exit(0)   exit(0)  return 0



編譯運(yùn)行結(jié)果:



從上面我們看到,test.txt的內(nèi)容為空.為什么呢?因?yàn)闃?biāo)準(zhǔn)I/O函數(shù)是帶緩存的,進(jìn)行fputs的時(shí)候是先向緩存中寫的,只有當(dāng)緩存滿的時(shí)候才會(huì)刷新的緩沖區(qū)的。從以上我們發(fā)現(xiàn),當(dāng)進(jìn)程退出時(shí),執(zhí)行_exit()函數(shù)并沒(méi)有刷新緩沖區(qū)的數(shù)據(jù),而是直接終止進(jìn)程的。

探究2.exit()



編譯運(yùn)行結(jié)果:


從上面我們可以看到,當(dāng)exit()函數(shù)結(jié)束進(jìn)程的時(shí)候,對(duì)緩存進(jìn)行了處理,把緩存的數(shù)據(jù)寫到了磁盤文件中。

探究3.return

由讀者自己完成,其實(shí)return語(yǔ)句用在main函數(shù)中,和exit是一樣的。但是我們知道,return返回的值是給調(diào)用者的,它代表著一個(gè)函數(shù)的結(jié)束。

四、exec函數(shù)族

exec.c  調(diào)用exec其中的一個(gè)函數(shù); gcc exec.c -o exec; ./exec
exec函數(shù)族提供了一種在進(jìn)程中啟動(dòng)另一個(gè)程序執(zhí)行的方法。它可以根據(jù)指定的文件名或目錄名找到可執(zhí)行文件,并用它來(lái)取代原調(diào)用進(jìn)程的數(shù)據(jù)段、代碼段、和堆棧段。在執(zhí)行完之后,原調(diào)用進(jìn)程的內(nèi)容除了進(jìn)程號(hào)外,其他全部都被替換了。

可執(zhí)行文件既可以是二進(jìn)制文件,也可以是任何Linux下可執(zhí)行的腳本文件。

何時(shí)使用?

當(dāng)進(jìn)程認(rèn)為自己不能再為系統(tǒng)和用戶做任何貢獻(xiàn)了就可以調(diào)用exec函數(shù)族中的函數(shù),讓自己執(zhí)行新的程序。
當(dāng)前目錄: 可執(zhí)行程序A    B(1,2,3)     
如果某個(gè)進(jìn)程想同時(shí)執(zhí)行另一個(gè)程序,它就可以調(diào)用fork函數(shù)創(chuàng)建子進(jìn)程,然后在子進(jìn)程中調(diào)用任何一個(gè)exec函數(shù)。這樣看起來(lái)就好像通過(guò)執(zhí)行應(yīng)用程序而產(chǎn)生了一個(gè)新進(jìn)程一樣。

execl("./B","B","1","2","3",NULL);
char *const envp[] = {"B","1","2","3",NULL}

execv("./B",envp);






注意:不管file,第一個(gè)參數(shù)必須是可執(zhí)行文件的名字

可執(zhí)行文件查找方式
表中的前四個(gè)函數(shù)的查找方式都是指定完整的文件目錄路勁,而最后兩個(gè)函數(shù)(以p結(jié)尾的函數(shù))可以只給出文件名,系統(tǒng)會(huì)自動(dòng)從環(huán)境變量"$PATH"所包含的路徑中進(jìn)行查找。

參數(shù)表傳遞方式
兩種方式:一個(gè)一個(gè)列舉和將所有參數(shù)通過(guò)指針數(shù)組傳遞
一函數(shù)名的第5個(gè)字母按來(lái)區(qū)分,字母"l"(list)的表示一個(gè)一個(gè)列舉方式;字母"v"(vector)的表示將所有參數(shù)構(gòu)造成指針數(shù)組傳遞,其語(yǔ)法為char *const argv[]

環(huán)境變量的使用
exec函數(shù)族可以默認(rèn)使用系統(tǒng)的環(huán)境變量,也可以傳入指定的環(huán)境變量。這里,以"e"(Envirment)結(jié)尾的兩個(gè)函數(shù)execle、execve就可以在envp[]中傳遞當(dāng)前進(jìn)程所使用的環(huán)境變量。

使用的區(qū)別
可執(zhí)行文件查找方式
參數(shù)表傳遞方式
環(huán)境變量的使用



案例一execl

#include <stdio.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
printf("start to execl.\n");
if(execl("/bin/ls","ls",NULL) < 0)
{
perror("Fail to execl");
return -1;
}
printf("end of execl.\n");

return 0;
}

運(yùn)行結(jié)果如下:




案例二、execlp
#include <stdio.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
printf("start to execl.\n");
if(execlp("ls","ls","-l",NULL) < 0)
{
perror("Fail to execl");
return -1;
}
printf("end of execl.\n");

return 0;
}

運(yùn)行結(jié)果:


案例三、execle

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
if(getenv("B") == NULL)
{
printf("fail to getenv B.\n");
}else{
printf("env B = %s.\n",getenv("B"));
}
if(getenv("C") == NULL)
{
printf("fail to getenv C.\n");
}else{
printf("env C = %s.\n",getenv("C"));
}

if(getenv("PATH") == NULL)
{
printf("fail to getenv PATH.\n");
}else{
printf("env PATH = %s.\n",getenv("PATH"));
}
return 0;
}

運(yùn)行結(jié)果:



#include <unistd.h>

int main(int argc,char *argv[])
{
printf("start to execle.\n");
char * const envp[] = {"B=hello",NULL};

if(execle("./A.out","A.out",NULL,envp) < 0)
{
perror("Fail to execl");
return -1;
}

printf("end of execl.\n");

return 0;
}

運(yùn)行結(jié)果:



案例四:execv

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
char * const arg[] = {"ps", "-ef", NULL};
//if (execl("/bin/ps", "ps", "-ef", NULL) < 0)
if (execv("/bin/ps" ,arg) < 0)
{
perror("execl");
exit(-1);
}

while (1);

return 0;
}


五、進(jìn)程的創(chuàng)建vfork()函數(shù)
     vfork與fork一樣都創(chuàng)建一個(gè)子進(jìn)程,但是它并不將父進(jìn)程的地址空完全復(fù)制到子進(jìn)程中,因?yàn)樽舆M(jìn)程會(huì)立即調(diào)用exec(或exit)于是也就不會(huì)存、訪該地址空間。不過(guò)在子進(jìn)程調(diào)用exec或exit之前,它在父進(jìn)程的空間中運(yùn)行。
    vfork和fork之間的另一個(gè)區(qū)別是:vfork保證子進(jìn)程先運(yùn)行,在它調(diào)用exec或exit之后 父進(jìn)程才可能被調(diào)度運(yùn)行。(如果在調(diào)用這兩個(gè)函數(shù)之前子進(jìn)程依賴于父進(jìn)程的進(jìn)一步動(dòng)作,則會(huì)導(dǎo)致死鎖)

探究1.vfork()



編譯運(yùn)行:



因?yàn)槲覀冎纕fork保證子進(jìn)程先運(yùn)行,子進(jìn)程運(yùn)行結(jié)束后,父進(jìn)程才開始運(yùn)行。所以,第一次打印的是子進(jìn)程的打印的信息,可以看到var值變成了89。子進(jìn)程結(jié)束后,父進(jìn)程運(yùn)行,父進(jìn)程首先打印fork調(diào)用返回給他pid的值(就是子進(jìn)程pid)。以上我們可以看出,vfork創(chuàng)建的子進(jìn)程和父進(jìn)程運(yùn)行的地址空間相同(子進(jìn)程改變了var 值,父進(jìn)程中的var值也進(jìn)行了改變)。

注意:如果子進(jìn)程中執(zhí)行的是exec函數(shù),那就是典型的fork的copy-on-wirte。


五、wait和waitpid

wait函數(shù):調(diào)用該函數(shù)使進(jìn)程阻塞,直到任一個(gè)子進(jìn)程結(jié)束或者是該進(jìn)程接收到一個(gè)信號(hào)為止。如果該進(jìn)程沒(méi)有子進(jìn)程或者其子進(jìn)程已經(jīng)結(jié)束,wait函數(shù)會(huì)立即返回。

waitpid函數(shù):功能和wait函數(shù)類似??梢灾付ǖ却硞€(gè)子進(jìn)程結(jié)束以及等待的方式(阻塞或非阻塞)。
wait函數(shù)
#include <sys/types.h>
#include <sys/waith.h>

pid_t  wait(int  *status);

函數(shù)參數(shù):

status是一個(gè)整型指針,指向的對(duì)象用來(lái)保存子進(jìn)程退出時(shí)的狀態(tài)。

A.status若為空,表示忽略子進(jìn)程退出時(shí)的狀態(tài)

B.status若不為空,表示保存子進(jìn)程退出時(shí)的狀態(tài)

子進(jìn)程的結(jié)束狀態(tài)可由Linux中一些特定的宏來(lái)測(cè)定。


案例一、

#include <stdio.h>
#include <stdlib.h>

int main()
{
int pid;
if((pid = fork()) < 0)
{
perror("Fail  to fork");
return -1;
}else if(pid == 0){
printf("child exit now.\n");
exit(0);
}else{
while(1);
}

exit(0);
}

運(yùn)行結(jié)果:



從以上可以看出,子進(jìn)程正常退出時(shí),處于僵尸態(tài)。這個(gè)時(shí)候子進(jìn)程的pid,以及內(nèi)核棧資源并沒(méi)有釋放,這樣是不合理的,我們應(yīng)該避免僵尸進(jìn)程。如果父進(jìn)程先退出呢,子進(jìn)程又會(huì)怎樣?

#include <stdio.h>
#include <stdlib.h>

int main()
{
int pid;
if((pid = fork()) < 0)
{
perror("Fail  to fork");
return -1;
}else if(pid == 0){
printf("child running now - pid : %d.\n",getpid());
while(1);
}else{
getchar();
printf("Father exit now - pid : %d.\n",getpid());
exit(0);
}

}



從上面可以看出,如果父進(jìn)程先退出,則子進(jìn)程的父進(jìn)程的ID號(hào)變?yōu)?,也就是說(shuō)當(dāng)一個(gè)子進(jìn)程的父進(jìn)程退出時(shí),這個(gè)子進(jìn)程會(huì)被init進(jìn)程自動(dòng)收養(yǎng)。

案例二、利用wait等待回收處于僵尸態(tài)的子進(jìn)程

#include <stdio.h>
#include <stdlib.h>

int main()
{
int pid;
if((pid = fork()) < 0)
{
perror("Fail  to fork");
return -1;
}else if(pid == 0){
printf("child runing now - pid : %d.\n",getpid());
getchar();
printf("child exiting now - pid : %d.\n",getpid());
exit(0);
}else{
printf("Father wait zombie now - pid : %d.\n",getpid());
wait(NULL);
printf("Father exiting now - pid : %d.\n",getpid());
exit(0);
}

}

沒(méi)有輸入字符前:


輸入字符后:



此時(shí)我們沒(méi)有發(fā)現(xiàn)僵尸進(jìn)程,當(dāng)子進(jìn)程退出時(shí),父進(jìn)程的wait回收了子進(jìn)程未釋放的資源。

案例三、獲取進(jìn)程退出時(shí)的狀態(tài)

#include <stdio.h>
#include <stdlib.h>

int main()
{
int pid;
int status;

if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(-1);
}else if(pid == 0){
printf("create child process : %d.\n",getpid());
printf("child process : %d calling exit(7).\n",getpid());
exit(7);
}else{

if((pid = fork()) < 0 ){
perror("Fail to fork");
exit(-1);
}else if(pid == 0){
printf("create child process : %d.\n",getpid());
while(1);
}else{

while((pid = wait(&status)) != -1)
{
if(WIFEXITED(status))
{
printf("child process %d is normal exit,the value is %d.\n",pid,WEXITSTATUS(status));
}else if(WIFSIGNALED(status)){
printf("child process %d is exit by signal,the signal num is %d.\n",pid,WTERMSIG(status));
}else{
printf("Not know.\n");
}
}
}
}
printf("All child process is exit,father is exit.\n");
exit(0);
}



給進(jìn)程15494發(fā)個(gè)信號(hào)



程序運(yùn)行結(jié)果:



從以上探究可以知道,每當(dāng)子進(jìn)程結(jié)束后,wait函數(shù)就會(huì)返回哪個(gè)子進(jìn)程結(jié)束的pid。如果沒(méi)有子進(jìn)程存在,wait函數(shù)就返回-1。

函數(shù)返回值:
成功:子進(jìn)程的進(jìn)程號(hào)
失敗:-1
#include <sys/types.h>
#include <sys/wait.h>

pid_t      waitpid(pid_t  pid,int *status,int options);

參數(shù):

1.在父進(jìn)程中創(chuàng)建兩個(gè)子進(jìn)程(A   B)
2.A進(jìn)程打印"child process %d exit",調(diào)用exit(2),結(jié)束
3.B進(jìn)程一直運(yùn)行

注意:父進(jìn)程調(diào)用while(waitpid(-1,&status,WUNTRACED)   != -1 )



status:同wait

options:

WNOHANG,若由pid指定的子進(jìn)程并不立即可用,則waitpid不阻塞,此時(shí)返回值為0
WUNTRACED,若某實(shí)現(xiàn)支持作業(yè)控制,則由pid指定的任一子進(jìn)程狀態(tài)已暫停,且其狀態(tài)自暫停以來(lái)還沒(méi)報(bào)告過(guò),則返回其狀態(tài)。

0:同wait,阻塞父進(jìn)程,等待子進(jìn)程退出。

返回值
正常:結(jié)束的子進(jìn)程的進(jìn)程號(hào)
使用選項(xiàng)WNOHANG且沒(méi)有子進(jìn)程結(jié)束時(shí):0
調(diào)用出錯(cuò):-1

案例一、

#include <stdio.h>
#include <stdlib.h>

int main()
{
int pid;
int status;

if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(-1);
}else if(pid == 0){
printf("create child process : %d.\n",getpid());
printf("child process : %d calling exit(7).\n",getpid());
exit(7);
}else{

if((pid = fork()) < 0 ){
perror("Fail to fork");
exit(-1);
}else if(pid == 0){
printf("create child process : %d.\n",getpid());
while(1);
}else{

while((pid = wait(&status)) != -1)
{
if(WIFEXITED(status))
{
printf("child process %d is normal exit,the value is %d.\n",pid,WEXITSTATUS(status));
}else if(WIFSIGNALED(status)){
printf("child process %d is exit by signal,the signal num is %d.\n",pid,WTERMSIG(status));
}else{
printf("Not know.\n");
}
}
}
}
printf("All child process is exit,father is exit.\n");
exit(0);
}


程序運(yùn)行結(jié)果:



使用ps -aux結(jié)果



從以上可以看出,子進(jìn)程15783退出時(shí),父進(jìn)程并沒(méi)有回收它的資源,此時(shí)可以看到它處于僵尸態(tài)。
由于父進(jìn)程調(diào)用waitpid等待子進(jìn)程15784退出,此時(shí)這個(gè)進(jìn)程還沒(méi)退出,看可以看到父進(jìn)程處于可中斷的睡眠狀態(tài)。

我們給子進(jìn)程15784發(fā)個(gè)信號(hào),在看看結(jié)果



程序運(yùn)行結(jié)果:



可以看到當(dāng)waitpid指定等待的進(jìn)程退出時(shí),waitpid立即返回,此時(shí)父進(jìn)程退出。

案例二、

#include <stdio.h>
#include <stdlib.h>

int main()
{
int pid;
int status;

if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(-1);
}else if(pid == 0){
printf("create child process : %d.\n",getpid());
printf("child process : %d calling exit(7).\n",getpid());
exit(7);
}else{

if((pid = fork()) < 0 ){
perror("Fail to fork");
exit(-1);
}else if(pid == 0){
printf("create child process : %d.\n",getpid());
while(1);
}else{
sleep(2);
printf("Father wait child %d exit.\n",pid);
while((pid = waitpid(pid,NULL,WNOHANG)))
{
printf("The process %d is exit.\n",pid);
}
printf("The process %d is exit.\n",pid);
}
}

exit(0);
}



從上面探究我們可以看出,如果有子進(jìn)程處于僵尸態(tài),waitpid(pid,NULL,WNOHANG)立即處理后返回,如果沒(méi)有子進(jìn)程處于僵尸態(tài),此時(shí)waitpid(pid,NULL,WNOHANG)也會(huì)立即返回,而不阻塞,此時(shí)返回值為0。

案例探究三、

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char *argv[])
{
int pid;
int status;

if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(-1);
}else if(pid == 0){
printf("create child process : %d.\n",getpid());
printf("child process in proces group %d.\n",getpgid(0));
printf("child process : %d calling exit(7).\n",getpid());
exit(7);
}else{

if((pid = fork()) < 0 ){
perror("Fail to fork");
exit(-1);
}else if(pid == 0){
sleep(3);
printf("create child process : %d.\n",getpid());
setpgid(0,0); //讓子進(jìn)程屬于以自己ID作為組的進(jìn)程組
printf("child process in proces group %d.\n",getpgid(0));
printf("child process : %d calling exit(6).\n",getpid());
}else{
while(pid = waitpid(0,NULL,0))
{
printf("Father wait the process %d is exit.\n",pid);
}
}
}

exit(0);
}

運(yùn)行結(jié)果:



當(dāng)在父進(jìn)中創(chuàng)建子進(jìn)程時(shí),父進(jìn)程和子進(jìn)程都在以父進(jìn)程ID號(hào)為組的進(jìn)程組。以上代碼中,有一個(gè)子進(jìn)程改變了自己所在的進(jìn)程組.
此時(shí)waitpid(0,NULL,0);只能處理以父進(jìn)程ID為組的進(jìn)程組中的進(jìn)程,可以看到第一個(gè)子進(jìn)程結(jié)束后waitpid函數(shù)回收了它未釋放的資源,而第二個(gè)子進(jìn)程則處于僵尸態(tài)















    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    日本高清不卡一二三区| 超薄肉色丝袜脚一区二区| 日韩精品视频一二三区| 国产情侣激情在线对白| 国产精品第一香蕉视频| 日韩在线一区中文字幕| 国产女同精品一区二区| 丰满人妻熟妇乱又乱精品古代| 久久夜色精品国产高清不卡| 国产成人精品资源在线观看| 日本在线高清精品人妻| 国产一区二区不卡在线播放| 麻豆精品视频一二三区| 97人妻精品一区二区三区男同| 日韩特级黄片免费在线观看 | 91人妻人人揉人人澡人| 国产又色又粗又黄又爽| 99精品国产自在现线观看| 日韩高清毛片免费观看| 美女被后入福利在线观看| 日韩高清一区二区三区四区| 亚洲黑人精品一区二区欧美| 久久久精品区二区三区| 懂色一区二区三区四区| 老司机精品国产在线视频| 精品日韩av一区二区三区| 午夜小视频成人免费看| 日韩欧美综合中文字幕| 中文字幕av诱惑一区二区| 91麻豆精品欧美一区| 午夜精品久久久99热连载| 国产永久免费高清在线精品| 欧美综合色婷婷欧美激情| 国产日韩欧美综合视频| 中文字幕在线区中文色| 久久免费精品拍拍一区二区| 在线观看免费无遮挡大尺度视频 | 日韩成人午夜福利免费视频| 国产欧美日韩在线一区二区| 国产精品午夜性色视频| 国产精品一区二区高潮|