僵尸进程

  僵尸进程一般出现在子进程中。如果子进程先于父进程退出,父进程没有调用wait()/waitpid()函数等待子进程结束来回收子进程资源,此时子进程处于“僵尸状态”,占用进程号和系统资源。可以通“ps”命令查看是否存在僵尸进程, 带有“< defunct >”标识的就是僵尸进程。


引起原因

  一个进程在调用exit()函数结束时,并没有真正的被销毁,部分占用的系统资源、进程号等并没有释放,系统中存在一个称为僵尸进程(zombie)的数据结构,该进程并不能参与调度,处于“僵尸状态”,仅仅在进程列表(processs table)中保留一个位置(slot),记录该进程的退出状态等信息。

  退出的进程需要其父进程来回收资源(俗称“收尸”),如果其父进程没创建SIGCHLD信号处理函数调用wait()/waitpid()等待子进程结束,又没有显式忽略该信号,该进程就一直处于僵尸状态。如果此时父进程结束了,init进程自动会“接管”这个子进程,退出僵尸状态。但是如果父进程是一个无限循环执行,那么子进程就会一直保持僵尸状态。

人为“创建”一个僵尸进程。

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


int main(int argc, char** argv)
{
	int pid ;
	
	pid = fork();
	
	if(pid < 0)
	{
		perror("fork error");
		return -1;
	}
	
	if(pid == 0)
	{
		/* todo */
		printf("Child process,PID is: %d,PPID is: %d.\n", getpid(), getppid());
		_exit(0);
	} 
	else if (pid > 0) 
	{
		printf("Parent process,PID is: %d.\n", getpid());
		for(;;)
		{
			sleep(1);	/* 父进程无限循环 */
		}
	}
	
	return 0 ;
}

java sh僵尸进程 进程僵尸状态_父进程


  人为故意产生僵尸进程,t1< defunc > (进程ID2363)为僵尸进程。

僵尸进程危害

  僵尸进程依然占用部分系统资源,如进程号;如果系统产生大量僵尸进程,系统会因为分配不到进程号而导致不能创建新的进程,这种后果是灾难性的。


僵尸进程释放

  僵尸进程使用“kill -9”命令是无法杀掉的,但可以杀掉(kill)其父进程来间接杀掉。父进程死后,僵尸进程成为"孤儿进程",init进程会接管孤儿进程并清理回收资源。如上面图中僵尸进程可以这样释放:

kill -9 2362


僵尸进程的避免

【1】父进程通过wait或者waitpid函数等待子进程结束;

【2】使用signal函数为SIGCHLD重写回调函数(handler),子进程结束后,父进程收到该信号,在信号handler中调用wait回收;

【3】如果父进程不关心子进程结束时间,可以使用signal(SIGCHLD、SIG_IGN等)通知内核,子进程结束后,由内核将其回收,而父进程则忽略该信号;

【4】特殊用法:fork两次,父进程fork一个子进程,子进程作为父进程再fork一 个子进程(孙进程?)然后退出,父进程wait子进程结束;子进程的子进程失去其父进程(第一个子进程),会被init进程接管,其结束后,被init进程回收,父进程无需再处理。

针对以上四种方式,编码例程:
【1】

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

int main(int argc, char** argv)
{
	int pid ;
	
	pid = fork();
	
	if(pid < 0)
	{
		perror("fork error");
		return -1;
	}
	
	if(pid == 0)
	{
		/* todo */
		printf("Child process,PID is: %d,PPID is: %d.\n", getpid(), getppid());
		_exit(0);
	} 
	else if (pid > 0) 
	{
		printf("Parent process,PID is: %d.\n", getpid());
		if (waitpid(pid, NULL, 0) < 0) 
		{
    		perror("waitpid");
    		return -1;
  		}
		for(;;)
		{
			sleep(1);
		}
	}
	
	return 0 ;
}

【2】

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

void child_handler(int signo)
{
  	printf("Receive process child signal\n");

  	if(waitpid(-1, NULL, 0) < 0) 	/* 等待所有子进程,相当于wait() */
  	{
    	perror("waitpid");
  	}
}

int main(int argc, char** argv)
{
	int pid ;
	
	if(signal(SIGCHLD, child_handler) == SIG_ERR) 	/* 安装 SIGCHLD信号 */
	{
    	perror("signal");
    	return -1;
  	}
  
	pid = fork();
	
	if(pid < 0)
	{
		perror("fork error");
		return -1;
	}
	
	if(pid == 0)
	{
		/* todo */
		printf("Child process,PID is: %d,PPID is: %d.\n", getpid(), getppid());
		_exit(0);
	} 
	else if (pid > 0) 
	{
		printf("Parent process,PID is: %d.\n", getpid());

		for(;;)
		{
			sleep(1);
		}
	}
	
	return 0 ;
}

【3】

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

int main3(int argc, char** argv)
{
	int pid ;
	
	if(signal(SIGCHLD, SIG_IGN) == SIG_ERR) 	/* 安装显式忽略信号,由init进程接管子进程释放 */
	{
    	perror("signal");
    	return -1;
  	}
  
	pid = fork();
	
	if(pid < 0)
	{
		perror("fork error");
		return -1;
	}
	
	if(pid == 0)
	{
		/* todo */
		printf("Child process,PID is: %d,PPID is: %d.\n", getpid(), getppid());
		_exit(0);
	} 
	else if (pid > 0) 
	{
		printf("Parent process,PID is: %d.\n", getpid());

		for(;;)
		{
			sleep(1);
		}
	}
	
	return 0 ;
}

【4】

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

int main(int argc, char** argv)
{
	int pid0,pid1;
	
	pid0 = fork();
	
	if(pid0 < 0)
	{
		perror("fork error");
		return -1;
	}
	
	if(pid0 == 0)
	{
		/* todo */
		printf("First Child process,PID is: %d,PPID is: %d.\n", getpid(), getppid());
		pid1 = fork();
		if(pid1 < 0)
		{
			perror("fork error");
			return -1;
		}
		else if(pid1 > 0)
		{
			printf("First Child process [%d] exit.\n", getpid());	/* 第一个子进程退出 */
   			_exit(0);
		}
		sleep(2);	/* 让父进程先运行 */
		
		printf("Second Child process,PID is: %d,PPID is: %d.\n", getpid(), getppid());		
		printf("Second Child process [%d] exit.\n", getpid());		/* 第一个子进程的子进程退出,由init进程接管 */
		_exit(0);
	} 
	else if (pid0 > 0) 
	{
		if (waitpid(pid0, NULL, 0) < 0) 	/* 等待第一个子进程退出 */
		{
    		perror("waitpid error");
    		return -1;
  		}
  		
		printf("Parent process,PID is: %d.\n", getpid());
		for(;;)
		{
			sleep(1);
		}
	}
	
	return 0 ;
}