Linux系统僵尸进程详解

系统 Linux
本文我们将来讨论一下什么是僵尸进程,僵尸进程是怎么产生的,如何杀死一个僵尸进程。

大安好,我是良许。

本文我们将来讨论一下什么是僵尸进程,僵尸进程是怎么产生的,如何杀死一个僵尸进程。

Linux中的进程是什么?

讲到进程,我们要先了解一下另一个概念:程序。

程序说白了就是躺在电脑硬盘上的一个文件而已(如同硬盘女神一样),在被 CPU 执行之前,它啥也做不了。

当程序被执行之后,它运行的实例就称为进程 。一个程序可以对应多个进程。

进程是系统的工作单元。系统由多个进程组成,其中有的是操作系统进程(执行系统代码),其他的是用户进程(执行用户代码)。所有这些进程都会并发执行,例如通过在单 CPU 上采用多路复用来实现。

你可以使用 ps 命令查看 Linux 系统中的所有进程 。 

  1. $ ps -ax  
  2.         PID TTY         STAT   TIME COMMAND  
  3.         1 ?     Ss      0:01 /usr/lib/systemd/systemd rhgb --switched-root --sys  
  4.         2 ?     S       0:00 [kthreadd]  
  5.         3 ?     I<      0:00 [rcu_gp]  
  6.         4 ?     I<      0:00 [rcu_par_gp] 

当一个进程调用 fork 函数生成另一个进程,原进程就称为父进程,新生成的进程则称为子进程。

Linux 系统中这样父子进程非常多,我们可以使用 pstree 命令查看系统上的进程「谱系」。 

  1. $ pstree -psn  
  2. systemd(1)─┬─systemd-journal(952)  
  3.         ├─systemd-udevd(963)  
  4.         ├─systemd-oomd(1137)  
  5.         ├─systemd-resolve(1138)  
  6.         ├─systemd-userdbd(1139)─┬─systemd-userwor(12707)  
  7.         │                     ├─systemd-userwor(12714)  
  8.         │                     └─systemd-userwor(12715)  
  9.         ├─auditd(1140)───{auditd}(1141)  
  10.         ├─dbus-broker-lau(1164)───dbus-broker(1165)  
  11.         ├─avahi-daemon(1166)───avahi-daemon(1196)  
  12.         ├─bluetoothd(1167) 

每个进程在系统中都被分配了一个编号。在这所有的进程中,有个非常特殊的进程,它的 ID 号是 1 。它是系统在引导过程中执行的第一个进程,PID 1 之后的每个后续进程都是它的后代。

什么是僵尸进程?

前面提到过,在 Linux 环境中,我们是通过 fork 函数来创建子进程的。创建完毕之后,父子进程独立运行,父进程无法预知子进程什么时候结束。

通常情况下,子进程退出后,父进程会使用 wait 或 waitpid 函数进行回收子进程的资源,并获得子进程的终止状态。

但是,如果父进程先于子进程结束,则子进程成为孤儿进程。孤儿进程将被 init 进程(进程号为1)领养,并由 init 进程对孤儿进程完成状态收集工作。

而如果子进程先于父进程退出,同时父进程太忙了,无瑕回收子进程的资源,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程,如下图所示:

僵尸进程是怎么产生的?

前面已经介绍了僵尸进程产生的原理,下面我们通过代码来模拟僵尸进程的产生。 

  1. #include    
  2. #include   
  3. #include    
  4. #include    
  5. int main(void)    
  6. {    
  7.     pid_t pid;  
  8.     pid = fork();    
  9.     if (pid == 0) {    
  10.             printf("I am child, my parent= %d, going to sleep 3s\n", getppid());    
  11.             sleep(3);    
  12.             printf("-------------child die--------------\n");    
  13.     } else if (pid > 0) {    
  14.             printf("I am parent, pid = %d, myson = %d, going to sleep 5s\n", getpid(), pid);    
  15.             sleep(5);    
  16.             system("ps -o pid,ppid,state,tty,command");   
  17.     } else {    
  18.         perror("fork");  
  19.         return 1;    
  20.     }    
  21.     return 0;    
  22. }   

在这个程序里,父进程创建子进程之后,就休眠 5 秒钟。而子进程只休眠 3 秒钟就退出,在它退出之后,父进程还未苏醒,因此没人给子进程「收尸」,所以它就变成了僵尸进程。

如何杀死僵尸进程

对于普通进程,我们可以通过使用 kill 命令来杀死它们。kill 命令它还有几个兄弟,比如 pkill 和 killall ,虽然它们名称里都带 kill 这样杀气腾腾的字眼,但它们实际上是被设计为向一个或多个进程发送信号。

在未指定的情况下,这几个命令默认发送的是 SIGTERM 信号。

普通进程可以被 kill ,但僵尸进程是不行的。为什么?因为僵尸进程本身就已经「死」过一次了!如果还可以再「死」,那「僵尸」这个名号就没多大意义了。

僵尸进程其实已经就是退出的进程,因此无法再利用kill命令杀死僵尸进程。僵尸进程的罪魁祸首是父进程没有回收它的资源,那我们可以想办法它其它进程去回收僵尸进程的资源,这个进程就是 init 进程。

因此,我们可以直接杀死父进程,init 进程就会很善良地把那些僵尸进程领养过来,并合理的回收它们的资源,那些僵尸进程就得到了妥善的处理了。

例如,如果 PID 5878 是一个僵尸进程,它的父进程是 PID 4809,那么要杀死僵尸进程 (5878),您可以结束父进程 (4809): 

  1. $ sudo kill -9 4809  #4809 is the parent, not the zombie 

杀死父进程时要非常小心,如果一个进程的父进程就是 PID 1 ,并且你还杀死了它,那么系统将直接重启!

这将是一个更可怕的故事! 

 

责任编辑:庞桂玉 来源: 良许Linux
相关推荐

2021-11-06 10:17:38

Linux僵尸进程

2021-10-25 12:23:06

Linux僵尸进程

2009-03-05 09:43:32

Linux系统进程管理命令

2017-12-15 09:40:47

Linux僵尸进程

2024-02-05 18:23:23

父进程应用程序程序

2021-11-01 12:13:53

Linux僵尸进程

2020-10-10 11:02:09

Linux 系统 数据

2022-05-30 11:50:45

僵尸网络Sysrv-K

2017-01-15 20:58:04

Linux进程作业管理

2010-06-04 14:49:15

Linux 查看进程

2019-02-25 09:50:25

Linux命令行僵尸进程

2010-03-03 09:16:17

2020-05-08 16:39:01

内存系统架构Linux

2012-05-08 11:01:45

linux守护进程

2013-01-15 15:18:46

Linux守护进程

2021-09-14 13:25:23

容器pod僵尸进程

2020-08-21 10:05:22

Linux系统结构内核

2021-01-14 15:41:22

LinuxSupervisor命令

2010-06-04 14:41:38

Linux 查看进程

2019-05-09 08:00:14

Linux服务器安全僵尸网络
点赞
收藏

51CTO技术栈公众号