Linux 编程之孤儿进程和僵尸进程

目录

1、孤儿进程

2、僵尸进程


1、孤儿进程

定义:没有父进程的进程,unix环境中,当父进程比子进程先退出,子进程变为孤儿进程,linux环境中的1号进程 "/sbin/init" 接管系统中的所有的孤儿进程,因此此时子进程的父进程变为1号进程,代码如下:

...
	if((pid = fork()) < 0)
	{
		printf("fork error\n");
	}
	else if(pid == 0)
	{
		printf("child:self_pid=%d,parent_pid=%d\n",getpid(),getppid());
		sleep(2);//等待父进程退出
		printf("parent exit,child:self_pid=%d,parent_pid=%d\n",getpid(),getppid());
		exit(0);
	}else{
		printf("parent:self_pid=%d,parent_pid=%d\n",getpid(),getppid());  
		sleep(1);
		printf("parent exit\n");
		exit(0);
	}
...

运行结果:

root@localhost some_func]# ./orphan_process
parent:self_pid=6463,parent_pid=30749
child:self_pid=6464,parent_pid=6463
parent exit//父进程退出
parent exit,child:self_pid=6464,parent_pid=1//原父进程退出,孤儿进程被1号进程接管

小结:父进程退出后,由1号进程接管对应的子进程,因此孤儿进程对于系统是无害的。

2、僵尸进程

定义:子进程先于父进程退出后,父进程并没有对子进程进行相应的回收处理。

代码如下:

...
	if((pid = fork()) < 0)
	{
		printf("fork error\n");
	}
	else if(pid == 0)
	{
		printf("child:self_pid=%d,parent_pid=%d\n",getpid(),getppid());
		exit(0);
	}else{
		printf("parent:self_pid=%d,parent_pid=%d\n",getpid(),getppid());

		while(1){
			sleep(3);
		} 
		exit(0);
	}
...

运行结果:

[root@localhost some_func]# ./zombie_process &
[2] 6564
[root@localhost some_func]# parent:self_pid=6564,parent_pid=30749
child:self_pid=6565,parent_pid=6564

[root@localhost some_func]# ps -ef | grep zombie_process
root      6564 30749  0 10:22 pts/3    00:00:00 ./zombie_process
root      6565  6564  0 10:22 pts/3    00:00:00 [zombie_process] 
root      6567 30749  0 10:22 pts/3    00:00:00 grep zombie_process
[root@localhost some_func]# cat /proc/6565/status
Name:   zombie_process
State:  Z (zombie)
Tgid:   6565
Pid:    6565
PPid:   6564
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0

如何避免:

1、产生僵尸进程后将父进程杀掉,所有资源被1号进程回收,因为无法直接通过 kill -9 杀死僵尸进程。

[root@localhost some_func]# ps -ef | grep zombie_process
root      6564 30749  0 10:22 pts/3    00:00:00 ./zombie_process
root      6565  6564  0 10:22 pts/3    00:00:00 [zombie_process] 
root      6759 30749  0 10:54 pts/3    00:00:00 grep zombie_process

//无法直接杀死僵尸进程
[root@localhost some_func]# kill -9 6565
[root@localhost some_func]# ps -ef | grep zombie_process
root      6564 30749  0 10:22 pts/3    00:00:00 ./zombie_process
root      6565  6564  0 10:22 pts/3    00:00:00 [zombie_process] 
root      6761 30749  0 10:54 pts/3    00:00:00 grep zombie_process

//杀死父进程曲线救国
[root@localhost some_func]# kill -9 6564
[root@localhost some_func]# ps -ef | grep zombie_process
root      6771 30749  0 10:55 pts/3    00:00:00 grep zombie_process
[2]+  Killed                  ./zombie_process
[root@localhost some_func]# ps -ef | grep zombie_process
root      6773 30749  0 10:55 pts/3    00:00:00 grep zombie_process
[root@localhost some_func]#

2、父进程中直接调用 wait/waitpid 回收子进程。

...
	if((pid = fork()) < 0)
	{
		printf("fork error\n");
	}
	else if(pid == 0)
	{
		sleep(1);
		printf("child:self_pid=%d,parent_pid=%d\n",getpid(),getppid());
		exit(0);
	}else{
		printf("parent:self_pid=%d,parent_pid=%d\n",getpid(),getppid());
		
		if(waitpid(pid,NULL,0) == -1)
		{
			printf("Child process recycled fail\n");
		}else{
			printf("Child process recycled successfully\n");
		}

		while(1){
			sleep(3);
		} 
		exit(0);
	}
...
[root@localhost some_func]# ./zombie_process &
[2] 6872
[root@localhost some_func]# parent:self_pid=6872,parent_pid=30749
child:self_pid=6873,parent_pid=6872
Child process recycled successfully

[root@localhost some_func]# ps -ef | grep zombie_process
root      6872 30749  0 11:01 pts/3    00:00:00 ./zombie_process
root      6875 30749  0 11:01 pts/3    00:00:00 grep zombie_process
[root@localhost some_func]#

3、注册子进程退出信号量SIGCHLD的钩子,在钩子中调用wait/waitpid回收子进程。

#include 
#include 
#include 
#include 

void sigchld_handler(int signo);

pid_t pid;
int main()
{
	//建立SIGCHLD信号(子进程退出信号)的处理函数
	signal(SIGCHLD, sigchld_handler);
	
	if((pid = fork()) < 0)
	{
		printf("fork error\n");
	}
	else if(pid == 0)
	{
		printf("child:self_pid=%d,parent_pid=%d\n",getpid(),getppid());
	    sleep(5);
		exit(0);
	}else{
		printf("parent:self_pid=%d,parent_pid=%d\n",getpid(),getppid());
		while(1){
			printf("parent running\n");
			sleep(1);
		} 
		exit(0);
	}

}

void sigchld_handler(int signo)
{
	if (signo != SIGCHLD)
	{
		printf("signo=%d,check fail\n",signo);
		return;
	}
		
	printf("child exit signo=%d\n",signo);
	
	if(waitpid(pid,NULL,0) == -1)
	{
		printf("Child process recycled fail\n");
	}else{
		printf("Child process recycled successfully");
	}
	return ;
}
[root@localhost some_func]# ./zombie_process
parent:self_pid=6921,parent_pid=30749
parent running
child:self_pid=6922,parent_pid=6921
parent running
parent running
parent running
parent running
child exit signo=17
Child process recycled successfullyparent running
parent running
parent running
parent running
parent running
parent running
^C
[root@localhost some_func]#

小结:从资源上讲,僵尸并不会占用什么系统资源,但是通常系统可以创建、管理的进程总数是有限的,如果有大量的僵尸进程占用着PID不干事,将会导致其它进程无法创建。从回收僵尸进程的方式讲,通过 杀死父进程间接回收或者在父进程直接调用waitpid(导致进程阻塞)都有一定的缺陷,通过注册SIGCHLD钩子处理无疑是最好的一种

你可能感兴趣的:(linux系统,linux系统编程,孤儿进程,僵尸进程,注册SIGCHLD信号处理函数,系统资源)