僵尸进程与孤儿进程

什么是僵尸进程

一个进程已经终止了,但是它的父进程还没有获取到其状态,那么这个进程就叫做僵尸进程。僵尸进程只保留最小的概要信息——一些基本内核数据结构,只消耗非常少的系统资源。保留这些概要信息主要是为了在父进程查询子进程的状态时可以提供相应的信息,僵尸进程等待父进程来查询自己的状态(这个过程称为在僵尸进程上等待)。一旦父进程得到了想要的信息,内核就会清除这些信息,僵尸进程就不存在了。

用 C 语言创建一个僵尸进程

[root@node01 tmp]# vi zombie.c 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
	pid_t pid;
	
	pid = fork();	// 创建进程
	if(pid < 0){ // 出错
		perror("fork");
	}
	
	if(0 == pid){ // 子进程
		printf("i am son\n");		
		exit(0); // 子进程退出
	}else if(pid > 0){ // 父进程
		printf("i am father\n");
		sleep(100); //父进程睡100秒后退出
		exit(0);
	}
	
	return 0;
}

[root@node01 tmp]# gcc zombie.c -o zombie
[root@node01 tmp]# ./zombie 
i am father
i am son

开启另一个终端查看进程状态

[root@node01 ~]# ps aux
...
root      2197  0.0  0.0   4160   340 pts/0    S+   09:01   0:00 ./zombie
root      2198  0.0  0.0      0     0 pts/0    Z+   09:01   0:00 [zombie] <defunct>  //僵尸进程
root      2200  0.0  0.1  47440  1688 pts/1    R+   09:01   0:00 ps aux

如何让僵尸进程消失

  • 直接干掉僵尸进程父进程(简单粗暴法)
  • 父进程去给子进程收尸(人性化方法)

父进程如何收尸(如何获取已退出进程的信息)

当一个进程终止时,内核会向其父进程发送SIGCHILD信号。默认情况下,父进程会忽略此信号量,也不会采取任何操作。但是,进程也可以选择通过signal() 或 sigaction()系统调用来处理这个信号。SIGCHILD信号可能会在任意时刻产生,并在任意时刻被传递给父进程,因为对于父进程而言,子进程的终止是异步的。

Linux内核提供了一些接口,可以获取已终止子进程的信息。其中最简单的一个是wait()。调用wait()成功时,会返回已终止子进程的pid;出错时,返回-1。如果没有子进程终止,调用会阻塞,直到有一个子进程终止。如果有个子进程已经终止了,调用会立即返回。因此,当得到子进程终止信息后——比如接收到SIGCHLD信号,调用wait()函数,就会立即返回,不会被阻塞。

用 C 语言获取退出进程的信息,编译并运行

[root@node01 tmp]# vi mywait.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
	pid_t pid;
	int status;
	
	pid = fork();	
	if(pid < 0){ 
		perror("fork");
	}
	
	if(0 == pid){ // 子进程
		printf("i am son\n");		
		exit(0); // 子进程退出
	}else if(pid > 0){ // 父进程
		printf("i am father\n");
		pid = wait (&status);        //获取子进程的信息
        if (pid == −1)
			perror ("wait");
		printf ("pid=%d\n", pid);    //输出子进程的 pid
		sleep(100); //父进程睡100秒后退出		
		exit(0);
	}
	
	return 0;
}

[root@node01 tmp]# gcc  mywait.c -o mywait
[root@node01 tmp]# ./mywait
i am father
i am son
pid=2526
Normal termination with exit status=0

重新开启一个终端,查看子进程是否存在

[root@node01 ~]# ps aux | grep Z
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root      2529  0.0  0.0   9032   656 pts/1    R+   10:34   0:00 grep --color=auto Z
[root@node01 ~]# ps aux | grep 2526
root      2531  0.0  0.0   9032   660 pts/1    R+   10:34   0:00 grep --color=auto 2526
[root@node01 ~]# 

在这个程序中,创建了一个子进程,然后子进程打印自己之后子进程立即退出。随后,父进程调用了系统调用wait()来获取子进程的状态。父进程会打印出子进程的pid。通过上面的实验可以发现同样是子进程先结束,但是父进程获取到了子进程退出的信息,所以系统中并没有僵尸进程的产生。

孤儿进程

孤儿进程和僵尸进程刚好相反,僵尸进程是子进程执行结束了,父进程还没结束,孤儿进程顾名思义,就是父进程结束了,子进程还没有结束,最后孤儿进程会被1号进程(init或systemd进程)收养,当然在子进程结束时也会由1号进程完成对它的状态收集工作,因此一般来说,孤儿进程并不会有什么危害。

创建一个孤儿进程

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

int main()
{
    pid_t  pid;
    pid = fork();

    if (pid < 0){ 
        perror("fork error:");
        exit(1);
    }else if (pid == 0){
                printf("son proess: [son id] = %d, [son's step father id] = %d\n", getpid(), getppid());
                sleep(100); 
                exit(0);

    }else if(pid > 0){  
                printf("i am father,I will go, take care of youself\n");
                printf("Bye my son,your father's pid is %d\n",getpid());
                exit(0);
        }

    return 0;
}

查看子进程的继父

[root@node01 ~]# pstree
systemd-+-NetworkManager-+-3*[dhclient]
        |                `-2*[{NetworkManager}]
        |-2*[agetty]
        |-auditd---{auditd}
        |-chronyd
        |-crond
        |-dbus-daemon
        |-httpd---5*[httpd]
        |-master-+-pickup
        |        `-qmgr
        |-orphen      //子进程的继父为 systemd
        |-polkitd---5*[{polkitd}]
        |-rsyslogd---2*[{rsyslogd}]
        |-sshd-+-sshd---bash---vi
        |      |-sshd---bash
        |      `-sshd---bash---pstree
        |-systemd-journal
        |-systemd-logind
        |-systemd-udevd
        `-tuned---4*[{tuned}]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值