僵尸进程 zombie

进程结束,父进程没有结束,且父进程没有调用wait/waitpid等函数获取子进程的状态信息,子进程的进程描述符(task_struct)仍然保存在系统中,这种进程叫僵尸进程。

危害:

  • 资源泄露,进程描述符被占用,而进程的pid的值是有范围的,如果大量的僵尸进程,会导致正常的进程可能无法创建。
  • 如何杀死: 杀死父进程,是僵尸进程变成孤儿进程,交由pid为1的进程回收PCB

linux下查看pid的范围: sysctl -a | grep pid_max

[root@iz2zecj7a5r32f2axsctb9z shell]# sysctl -a | grep pid_max
kernel.pid_max = 32768

实例

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

int main(int argc, char *argv[]) {
    pid_t pid = fork();
    if(pid == 0) {

    } else if(pid > 0) {
        printf("parent process\n");
        sleep(30);
    } else {
        perror("fork");
        return -1;
    }
    return 0;
}

子进程结束,父进程睡眠30s, 子进程结束时,父进程仍在运行,在linux下通过 ps aux | grep zombie 可以看到:

[root@iz2zecj7a5r32f2axsctb9z shell]# ps aux | grep zombie
root      7368  0.0  0.0   4216   352 pts/0    S+   12:11   0:00 ./zombie
root      7369  0.0  0.0      0     0 pts/0    Z+   12:11   0:00 [zombie] <defunct>

状态标记:

  • S 表示可被中断的睡眠状态
  • Z 表示僵尸进程,进程运行完了,等待资源回收
    • 表示进程组

僵尸进程何时被销毁: 父进程结束,会将子进程的父进程pid修改成1,然后交由1号进程进行回收。

如何避免僵尸进程

可以将子进程fork 2次 把子进程退出,使子进程创建的子进程成为孤儿进程,这样就能避免产生僵尸进程。
如何避免僵尸进程

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {

    pid_t pid;
    if((pid = fork()) < 0){
        perror("fork");
        return -1;
    } else if (pid == 0) {
        if ((pid = fork()) < 0 ) {
            perror("fork");
            return -1;
        } else if(pid > 0) {
            exit(0);
        }
        sleep(10);
        printf("second child, parent pid=%d\n", getppid());
        exit(0);
    }

    if(waitpid(pid, NULL, 0) != pid) {
        printf("waitpid error\n");
        return -1;
    }
    return 0;
}

运行结果:

second child, parent pid=1

文档更新时间: 2021-03-06 13:05   作者:周国强