僵尸进程与孤儿进程


什么是僵尸进程,什么是孤儿进程,会带来什么问题,如何解决?

基本概念

子进程由父进程创建,而子进程和父进程的运行是异步的,谁也不确定谁先运行,当子进程完成工作时,父进程需要使用 waitwaitpid 来获取子进程终止状态

孤儿进程 👶

产生原因

父进程先于子进程退出(没有使用 waitwaitpid),子进程还在运行,则这些子进程会变成孤儿进程,将被 init 进程(pid 为 1)收养,并由 init 对其进行状态收集

危害

孤儿进程由 init 进程收养,并不会有什么危害

代码

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

int main(void) {
    // 对于 fork,父进程返回子进程 pid,子进程返回 0,失败返回 -1
    pid_t pid = fork();
    if (pid < 0) {
        perror("Fork Error");
        exit(1);
    } else if (pid == 0) { // 子进程
        printf("I'm the child process\n");
        printf("pid: %d\tppid: %d\n", getpid(), getppid());
        sleep(3); // 确保此时父进程已退出,被 init 进程收养
        printf("pid: %d\tppid: %d\n", getpid(), getppid());
        printf("child process is exited\n");
    } else { // 父进程
        printf("I'm the father process\n");
        sleep(1); // 确保子进程先输出当前的 ppid
        printf("father process is exited\n");
    }
    return 0;
}
I'm the father process
I'm the child process
pid: 27788      ppid: 27787
father process is exited
pid: 27788      ppid: 1
child process is exited

僵尸进程 🧟‍♀️

产生原因

子进程退出,父进程没有调用 waitwaitpid 获取子进程状态,则子进程的进程描述符会一直占用,被称为僵尸进程

危害

僵尸进程的进程描述符会一直占用,如产生大量僵尸进程会导致可用的 pid 大量减少,甚至不能产生新进程,且会占用空间

解决方案

僵尸进程的罪魁祸首是不回收子进程的父进程,可以 kill 发送 SIGKILLSIGTERM 来枪毙他,让 init 进程回收这些孤儿进程,使这些僵死的孤儿进程瞑目

代码

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

int main(void) {
    // 对于 fork,父进程返回子进程 pid,子进程返回 0,失败返回 -1
    pid_t pid = fork();
    if (pid < 0) {
        perror("Fork Error");
        exit(1);
    } else if (pid == 0) { // 子进程
        printf("I'm the child process\n");
        printf("pid: %d\tppid: %d\n", getpid(), getppid());
        printf("child process is exited\n");
        exit(0);
    }
    printf("I'm the father process\n");
    sleep(1); // 确保子进程先输出当前的 ppid
    system("ps -o pid,ppid,state,tty,command | grep zombie | grep -v grep");
    printf("father process is exited\n");
    return 0;
}
I'm the father process
I'm the child process
pid: 30196      ppid: 30195
child process is exited
30195 11875 S+   ttys008  /Users/world/code/tmp/zombie
30196 30195 Z+   ttys008  (zombie)
father process is exited

大量产生僵尸进程

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

int main(void) {
    while (1) {
        // 对于 fork,父进程返回子进程 pid,子进程返回 0,失败返回 -1
        pid_t pid = fork();
        if (pid < 0) {
            perror("Fork Error");
            exit(1);
        } else if (pid == 0) { // 子进程
            printf("I am a child process.\nI am exiting.\n");
            exit(0);
        }
        sleep(1); // 确保子进程先输出当前的 ppid
        system("ps -o pid,ppid,state,tty,command | grep zombie | grep -v grep");
    }
    return 0;
}
I am a child process.
I am exiting.
30960 11875 S+   ttys008  /Users/world/code/tmp/zombie2
30965 30960 Z+   ttys008  (zombie2)
I am a child process.
I am exiting.
30960 11875 S+   ttys008  /Users/world/code/tmp/zombie2
30965 30960 Z+   ttys008  (zombie2)
30974 30960 Z+   ttys008  (zombie2)
I am a child process.
I am exiting.
30960 11875 S+   ttys008  /Users/world/code/tmp/zombie2
30965 30960 Z+   ttys008  (zombie2)
30974 30960 Z+   ttys008  (zombie2)
30983 30960 Z+   ttys008  (zombie2)
I am a child process.
I am exiting.
30960 11875 S+   ttys008  /Users/world/code/tmp/zombie2
30965 30960 Z+   ttys008  (zombie2)
30974 30960 Z+   ttys008  (zombie2)
30983 30960 Z+   ttys008  (zombie2)
30992 30960 Z+   ttys008  (zombie2)
I am a child process.
I am exiting.
30960 11875 S+   ttys008  /Users/world/code/tmp/zombie2
30965 30960 Z+   ttys008  (zombie2)
30974 30960 Z+   ttys008  (zombie2)
30983 30960 Z+   ttys008  (zombie2)
30992 30960 Z+   ttys008  (zombie2)
31001 30960 Z+   ttys008  (zombie2)
I am a child process.
I am exiting.
30960 11875 S+   ttys008  /Users/world/code/tmp/zombie2
30965 30960 Z+   ttys008  (zombie2)
30974 30960 Z+   ttys008  (zombie2)
30983 30960 Z+   ttys008  (zombie2)
30992 30960 Z+   ttys008  (zombie2)
31001 30960 Z+   ttys008  (zombie2)
31014 30960 Z+   ttys008  (zombie2)
I am a child process.
I am exiting.
30960 11875 S+   ttys008  /Users/world/code/tmp/zombie2
30965 30960 Z+   ttys008  (zombie2)
30974 30960 Z+   ttys008  (zombie2)
30983 30960 Z+   ttys008  (zombie2)
30992 30960 Z+   ttys008  (zombie2)
31001 30960 Z+   ttys008  (zombie2)
31014 30960 Z+   ttys008  (zombie2)
31023 30960 Z+   ttys008  (zombie2)
I am a child process.
I am exiting.
^C

最后使用 Ctrl-C 退出,僵尸进程被 init 收养释放,也可以使用 kill -9 <pid>

总结

无论是孤儿进程还是僵尸进程产生的原因都是父进程没有 waitwaitpid,区别在于孤儿进程是父进程先走,僵尸进程是子进程先走

参考

孤儿进程与僵尸进程[总结]


文章作者: Mou shuai
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Mou shuai !
评论
  目录