Linux多進程編程之 孤兒進程&殭屍進程+wait函數

我們可否想過一個問題:使用fork()函數創建子進程,因爲父進程和子進程的執行順序是隨機的

當父進程已經結束了,子進程還會繼續存在並正常執行嗎?

我們先看這個例子:

guer1.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
 
int main()
{
	pid_t pid = fork(); 
	if(pid<0)
		perror("fork error!");    
	else if (pid>0)
		printf("I am parent , pid is %d\n",getpid());
	else{
		sleep(2);//保證父進程優先結束
		printf("I am child , pid is %d\n",getpid());
	}  
    
	return 0;
} 

我們控制父進程優先執行,可以看到子進程還是可以執行,但是爲什麼把輸出越界了呢,而不是正常的在兩個指令行之間輸出?

因爲父進程結束的時候我們程序就退出了,而兩個指令行之間的空間是我們上面執行的程序的空間,子進程執行的位置發生了變化,說明他已經不在屬於剛纔的父進程了,那是怎麼肥四呢?

其實當父進程執行完畢之後,就結束了,子進程的爹沒了,他就變成了一個孤兒進程,那他怎麼辦呢?他找到了他的 太太太太太爺爺,init進程( 在Linux系統中,當內核啓動了自己之後,init是第一個用戶進程,它的進程號是1,是系統中所有其他用戶進程的祖先進程 ),這時候init進程會帶領被父親拋棄的孤兒進程執行完畢後退出。

我們可以看一下下面這個程序對這個問題的實踐

guer2.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
	pid_t pid=fork();
	if(pid<0)
		perror("fork error!");
	else if(pid==0){//child 
		printf("I am child process \n");
		printf("pid:%d----ppid:%d\n",getpid(),getppid());
		printf("I will sleep for 3 seconds\n");
		sleep(3);
		printf("pid:%d----ppid:%d\n",getpid(),getppid());
		printf("child process exit\n");
	}
	else{//parent 
		printf("I am father process\n");
		sleep(1);//讓子進程執行到sleep
		printf("father process exit\n");	
	}

	return 0;
}

我們可以看到父進程結束後,子進程的ppid變成了1,成功地被init進程收養了

剛纔說的是父進程優先執行完成,那麼子進程優先完成的時候怎麼辦呢?也是直接退出嗎?

答案是不是的

正常情況下,子進程是通過父進程創建的,子進程的結束和父進程的運行是一個異步過程,即父進程無法預測子進程 到底什麼時候結束。

unix提供了一種機制可以保證只要父進程想知道子進程結束時的狀態信息,就可以得到。

這種機制就是: 在每個進程退出的時候,內核會向父進程發送SIGCHLD信號,內核釋放該進程所有的資源,包括打開的文件,佔用的內存等。 但是仍然爲其保留一定的信息(包括進程號,退出狀態,運行時間等),保留的這些信息就是所謂的殭屍進程!

我們可以看一下殭屍的狀態:

jiangshi.c

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

int main()
{
	pid_t pid=fork();
	if(pid<0)
		perror("fork error!");
	else if(pid==0){
		printf("I am child process,I exit\n");
		exit(0);
	}
	printf("I am father process\n");
	printf("I will sleep for 2 seconds\n");
	sleep(2);//等待子進程退出
	system("ps -o pid,ppid,state,tty,command");//輸出進程信息 
	printf("father process exit\n");
	return 0;
}

Z的意思就是 Zombie,即殭屍

根據殭屍進程的定義我們可以知道,只要是創建了子進程,一定會產生殭屍進程,所以殭屍進程並不可怕,但是殭屍進程是佔內存的,我們知道一般進程只能同時存在300-400個左右,假如我們寫了一個非常大的程序,需要不斷的創建非常多的子進程,正常情況下我們渴望的是讓他們輪流存活,也就是交替的創建和銷燬,但是如果我們沒能及時的銷燬這些殭屍進程導致累積過多的話,就會使得後面的進程創建失敗而使得程序作廢

那麼殭屍進程是怎麼被處理的呢?Linux提供了一個wait()函數,wait很明顯是等待的意思,但是他和sleep()不一樣,不是掛起不執行,而是等待子進程一起結束,他可以使父進程獲得子進程的信息,在父進程執行完成退出之前銷燬子進程產生的殭屍進程,關於wait()的機制可以看下面的這兩個圖,就不再詳細講了。。

其實是兩個函數wait()和waitpid(),二者的作用比較類似,都是處理殭屍進程,但是調用的方法及作用也有區別,我們來看一下:

pid_t wait(int* status)

進程一旦調用了wait,就立即阻塞自己,由wait自動分析是   否當前進程的某個子進程已經退出,如果讓它找到了這樣一個已經變成殭屍的子進程,wait就會收集這個子進程的信息,並把它徹底銷燬後返回,返回值是銷燬的子進程的id;如果沒有找到這樣一個子進程,wait就會一直阻塞在這裏,直到有一個出現爲止。  參數status用來保存被收集進程退出時的一些狀態,它是一個指向int類型的指針。但如果我們對這個子進程是如何死掉的毫不在意,只想把這個殭屍進程消滅掉,我們就可以設定這個參數爲NULL ,想知道的話是可以百度的到的,嘻嘻

pid = wait(NULL); 

wait1.c

#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	pid_t pc,pr;
	pc=fork();
	if(pc<0)
		perror("creat process error");
	else if(pc==0){
		printf("child process,pid is %d\n",getpid());
		sleep(10);
	}
	else {
		pr=wait(NULL);
		printf("parent proccess,my child pid is %d\n",pr);
	}
	exit(0);
}

這樣父進程就可以獲得子進程的pid

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

返回值和參數status和wait()函數一樣,

pid:

pid>0時,只等待進程ID等於pid的子進程,不管其它已經有多少子進程運行結束退出了,只要指定的子進程還沒有結束,waitpid就會一直等下去。

pid==-1時,等待任何一個子進程退出,沒有任何限制,此時waitpid和wait的作用一模一樣。

pid==0時,等待同一個進程組中的任何子進程,如果子進程已經加入了別的進程組,waitpid不會對它做任何理睬。

pid<-1時,等待一個指定進程組中的任何子進程,這個進程組的ID等於pid的絕對值。

options:

options提供了一些額外的選項來控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED兩個選項,這是兩個常數,如果使用了WNOHANG(wait no hung)參數調用waitpid,即使沒有子進程退出,它也會立即返回,不會像wait那樣永遠等下去。而WUNTRACED參數,由於涉及到一些跟蹤調試方面的知識,加之極少用到, 如果我們不想使用它們,也可以把options設爲0

pid=waitpid(-1,NULL,0);

waitpid1.c

#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	pid_t pc,pr;
	pc=fork();
	if(pc<0)
		perror("creat process error");
	else if(pc==0){
		printf("child process,pid is %d\n",getpid());
		sleep(10);
	}
	else {
		pr=waitpid(-1,NULL,0);
		printf("parent proccess,my child pid is %d\n",pr);
	}
	exit(0);
}

我們使得waitpid和wait函數取得了相同的效果,贊!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章