【Linux】僵尸进程 + 孤儿进程 + 守护进程

三种特殊进程状态

僵尸进程

  • 基本概念

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程子进程的资源未得到释放,此时子进程将成为一个僵尸进程

  • 危害

Unix中的一种特殊机制:每个进程退出的时候,内存释放该进程的所有资源,但仍为其保留一定的信息(这些信息包括:进程号、退出状态、运行时间等),这些信息直到父进程通过 wait / waitpid 来取得时才会被释放。

由于 子进程的结束父进程的进行 是一个 异步过程,如果父进程一直不调用 wait / waitpid 来取得已退出子进程的保留信息,这就导致了该子进程的保留信息无法被释放,直接导致子进程的进程号无法被释放,但是操作系统的进程号是有限的,如果大量产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。

  • 避免方法
  1. 父进程通过 wait/waitpid等函数挂起,一直等待子进程的结束。
  2. 如果父进程很忙不能被挂起,可以使用signal函数将一个信号处理函数指针handler与SIGCHLD进行绑定,在信号处理函数中调用 wait/waitpid 进行回收子进程残留信息
  3. 如果父进程不关心子进程啥时候结束,可以使用signal(SIGHLD,SIG_IGN) 通知内核,父进程不care子进程的状态,那么子进程结束后,内核就会将子进程残留信息进行回收,并不再给父进程发送SIGHLD信号
  4. 小技巧:fork两次。父进程fork一个子进程A,然后父进程继续自己的工作,子进程A fork 出孙进程B,然后子进程A立即退出,孙进程B会被init进程接管,孙进程B结束后会被init回收,不用经过父进程来回收,但是子进程A的回收还是需要父进程来处理
  • 处理方法

如果不慎产生了僵尸进程,我们该怎么办呢?

解决方法:杀死僵尸进程的父进程(僵尸进程的父进程必然存在),进而使僵尸进程变成孤儿进程,过继给1号进程init,init进程会始终负责清理僵尸进程

  • 查看方法

我们在Linux下运行以下代码,可以产生一个僵尸进程

程序解释:main线程中fork了一个子进程,子进程执行完打印语句后执行exit进行线程退出,但父进程一直处于睡眠状态,无法回收子进程信息,导致子进程变成僵尸进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
    
    
    pid_t child = fork();
    if(child < 0)
        perror("fork process fail!\n");
    else if(child == 0)
    {
    
    
        printf("fork succ,this run in child process, Child PID = %d\n",getpid());
        exit(0);
    }
    else
    {
    
    
        printf("this run in parent process, Parent PID = %d\n",getpid());
        sleep(600);
    }                                                                                                                                                                     

    return 0;
}

运行之后:发现产生了一个进程状态为Z的a.out进程,这个进程就是僵尸进程

image.png

image.png

ps命令:显示所有活动进程的信息

使用方法: ps aux | grep 进程名称


孤儿进程

  • 定义

孤儿进程指的是其父进程执行完成或被终止后仍继续运行的一类进程。

这些孤儿进程被init进程所收养,并由init进程对他们完成状态收集工作

  • 简介

在类Unix操作系统中,子进程是通过父进程创建的。

子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底啥时候结束。

当一个父进程退出,而他的一个或多个子进程还在运行,那么这些子进程将会变成孤儿进程,过继给init进程(进程号为1),并由init进程对他们完成状态收集工作。

每当出现一个孤儿进程的时候,内核会把孤儿进程的父进程设置为init进程,而init会循环的调用wait()处理这些孤儿进程后续退出工作

  • 模拟产生孤儿进程

程序解释:main进程fork出一个子进程,睡眠1ms后立刻退出,子进程睡眠6ms,导致父进程先于子进程退出,子进程变成孤儿进程

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

main()
{
    
    
    pid_t pid;
    pid = fork();
    if(pid < 0)
        printf("error occurred!/n");
    else if(pid == 0) {
    
    
        sleep(6);
        printf("I'm a orphan/n");
        exit(0);
    }
    else {
    
    
        sleep(1);
        printf("Children Bye!/n");
    }
} 

孤儿进程没有任何危害


守护进程

  • 定义

守护进程(daemon)是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。

翻译成人话:在Linux系统中,我们会发现在系统启动的时候有很多的进程就已经开始跑了,也称为服务,这也是我们所说的守护进程。

守护的特征:

  1. 进程脱离终端(原因:为了防止运行过程中的信息在终端上面显示)
  2. 后台运行

守护进程的生命周期是从系统开始运行系统停止运行

  • 查看

使用 ps axj 来查看当前系统有哪些守护进程正在运行

  • a 表示不仅列出当前用户进程,也列出其他所有用户的进程
  • x 表示不仅列出有控制终端的进程,也列出没有控制终端的进程
  • j 表示列出与作业控制相关的信息

image.png

从图中的守护进程可以看出以下特点:

  • 守护进程都是被超级用户启动(UID为0)
  • 没有控制终端(TTY为 ?)
  • 终端进程组 ID 为-1 (TPGID表示终端进程组)

猜你喜欢

转载自blog.csdn.net/weixin_45437022/article/details/109660825