僵尸进程与孤儿进程


概述

1. 除了pid = 0以外的所有进程,都是由其他进程调用fork创建的,也就是说所有进程都是有父进程的

2. 子进程和父进程的运行是一个异步状态,也就是父亲永远不知到儿子啥时候会死,当一个子进程完成任务死亡后,它的进程控制块PCB仍然保留在内存中,它的爸爸需要调用wait()或waitpid系统调用来得知儿子的状态

3. 儿子死后会告诉利用返回码告诉操作系统,操作系统就告知儿子的父亲自己的儿子死了,利用SIGCHLD信号,父亲会使用系统wait系统调用读取儿子的退出状态,只有父进程读取后,内核才会把儿子的PCB给释放掉(收尸+火化)

4. 如果父亲一直不调用wait或waitpid函数读取儿子的状态,那么内核就无法为儿子收尸和火化,只能等着它的父亲知道儿子的状态后才能收尸+火化,或者说父亲也死了的时候才会一同为他俩收尸和火化

5. 儿子死了,但爸爸迟迟不去调用wait或waitpid读取儿子的结束状态,导致儿子的PCB一直留在内存中,就是一直无法为儿子收尸,因为父亲还没有看儿子最后一眼,内核实在不忍心收尸火化,这个时候子进程就是僵尸进程

6. 另一种情况,就是爸爸死了儿子还没死,这个时候儿子就成了孤儿,叫做孤儿进程,那它只能被送到孤儿院(init进程,进程号为1)来收养它,当子进程结束后,init进程会循环使用wait()函数,因此子进程死掉后,内核就会回收,不会向僵尸进程那样占用这内存

查看代码

#include
#include
#include

int main(){
    printf("pid = %d\n",getpid());
    pid_t pid = fork();
    if(pid == 0){
        while(1)
        {
            printf("father pid = %d\n", getppid());
            printf("child pid = %d\n",getpid());
            sleep(1);
        }
    }
    else if(pid > 0){
        printf("This is the parent!pid = %d\n",getpid());
        sleep(3);
        exit(0);
    }
    return 0;
}

查看代码

#include
#include
#include

int main(){
    printf("pid = %d\n",getpid());
    pid_t pid = fork();
    if(pid == 0){
        printf("father pid = %d\n", getppid());
        printf("child pid = %d\n",getpid());
        sleep(1);
    }
    else if(pid > 0){
        printf("This is the parent!pid = %d\n",getpid());
        sleep(5);
        exit(0);
    }
    return 0;
}

  • 僵尸进程和孤儿进程有哪些危害:
  1. 僵尸进程会占用系统资源,过多的僵尸进程可能会影响服务器性能
  2. 孤儿进程没啥危害,孤儿进程还活着,被送进了孤儿院(init进程管理)
  • 僵尸进程如何避免:
  1. 父进程调用wait或waitpid等待子进程结束
查看代码

#include
#include
#include 
#include 

int main(){
    printf("pid = %d\n",getpid());
    pid_t pid = fork();
    if(pid == 0){
        printf("father pid = %d\n", getppid());
        printf("child pid = %d\n",getpid());
        sleep(1);
        exit(100);
    }
    else if(pid > 0){
        printf("This is the parent!pid = %d\n",getpid());
        sleep(5);
        int status = 0;
        int child_pid = wait(&status);
        std::cout<<"child pid is "<

  1. fork两次,父进程fork出子进程,子进程fork出孙进程,然后子进程就退出,此时孙进程就变成了孤儿,它爷爷才不会管他呢,所以只能进孤儿院交给init进程,所以它退出后就不会变成僵尸进程,但是子进程还是需要父进程来回收
  2. 若父进程比较忙,不想调用wait或waitpid来挂起等待,那么它就可以使用SIGCHLD信号,当儿子死后,内核会告知它爹,然后父进程在信号处理函数中再调用wait或waitpid函数就可以了,这样儿子就不是僵尸进程了
查看代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
void process(int signo)
{
	int status;
	pid_t pid;
	pid = waitpid(0, &status, WNOHANG);
    std::cout<<"child dead pid is "< 0){
		struct sigaction act;
		act.sa_handler = process;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		//对SIGCHLD信号的解释:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
		//sigaction函数的作用是修改信号处理动作,不让执行默认动作,去执行sigaction函数注册的回调函数
		sigaction(SIGCHLD, &act, NULL);
		while(1){
			printf("parent ID %d\n",getpid());
			sleep(1);
		}
        return 0;
	}	
}

3. 在父进程中使用signal(SIGCHLD, SIGIGN)函数,告诉内核,他不关心这儿子的死活,死了就直接收尸埋了算了,所以儿子死后,内核会释放其PCB不用等它爸再看最后一眼了

查看代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
int main()
{
	pid_t pid;

	pid = fork();
	if(pid == 0){
        std::cout<<"child pid is "< 0){
        signal(SIGCHLD,SIG_IGN);  //忽略SIGCHLD信号
		while(1){
			printf("parent ID %d\n",getpid());
			sleep(1);
		}
        return 0;
	}	
}

  • 僵尸进程的查看
  1. 查看僵尸进程:ps -aux | grep Z
  • 僵尸进程的处理

  1. 找到僵尸进程的父进程:

参考文献

1. 进程3.0——进程状态与僵尸进程、孤儿进程