Linux 进程状态 — 僵尸进程



🎁个人主页: 工藤新一¹

🔍系列专栏: C++面向对象(类和对象篇)

🌟心中的天空之城,终会照亮我前方的路

🎉欢迎大家点赞👍评论📝收藏⭐文章


文章目录

  • [进程状态 --- 僵尸进程](#进程状态 — 僵尸进程)
    • [一、进程状态 --- Zombies](#一、进程状态 — Zombies)
    • 二、僵尸进程
      • [2.1 什么是僵尸进程?](#2.1 什么是僵尸进程?)
      • [2.2 为什么会有僵尸进程?](#2.2 为什么会有僵尸进程?)
      • [2.3 僵尸进程的危害?](#2.3 僵尸进程的危害?)
      • [2.4 如何产生一个僵尸进程?](#2.4 如何产生一个僵尸进程?)
      • [2.5 如何清除僵尸进程?](#2.5 如何清除僵尸进程?)
      • [2.6 如何避免僵尸进程?](#2.6 如何避免僵尸进程?)

进程状态 --- 僵尸进程

一、进程状态 --- Zombies

  • Z (Zombies) :进程已经执行完毕并终止了,但其父进程还没有调用 wait()waitpid() 系统调用来回收它子进程退出状态信息。此时,这个已经死亡的进程就成为了 "僵尸进程"
  • 重要:僵尸进程已经不占用任何内存或CPU资源,它仅仅在进程表中保留一个入口,记录其退出状态,供父进程查询。如果父进程一直不回收,它就会一直存在 -(僵死进程会以终⽌状态保持在进程表中,并且会⼀直在等待父进程读取退出状态代码)
  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入 Z(Zombies) 状态

二、僵尸进程

2.1 什么是僵尸进程?

故事插入:

如上所述,僵尸进程 是一个已经终止但尚未被其父进程回收的进程。


2.2 为什么会有僵尸进程?

这是Unix系统进程状态设计的一部分。设置僵尸状态是为了维护进程的退出信息,以便父进程能够查询子进程是如何死掉的(是正常退出还是被信号杀死?退出码是多少?)。

只有当父进程读取了这些信息后,内核才会彻底清除这个僵尸进程。



1.那些信息:进程退出时,必须要维持进程的基本相关信息(哪些基本信息?),让父进程得知其退出情况

2.信息存在哪里?当进程退出时,其退出信息是要保存在 task_struct 中(因为代码、数据已经被释放了,就只剩下 PCB 了),必须维持PCB(操作系统中的数据结构)。我们可以通过父进程的系统调用获取到子进程的特殊信息,我们就知道这个进程是因为什么原因而退出的。所以在子进程已经退出之后,父进程没有获取子进程的信息之前,这个子进程就是僵尸进程


2.3 僵尸进程的危害?

  • 资源占用:僵尸进程本身不消耗内存和CPU。
  • 主要危害 :每个僵尸进程都会在系统的进程表中占用一个槽位(即一个进程ID,PID)。如果系统中存在大量僵尸进程,进程表会被填满,导致系统无法创建新的进程,从而引发严重问题。
  • 内存泄漏问题

2.4 如何产生一个僵尸进程?

一个典型的例子:父进程创建了子进程,但父进程忙于自己的工作,没有调用 wait() 来等待子进程结束。子进程先于父进程结束,就会变成僵尸。

简单C代码示例:

cpp 复制代码
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main () {
  pid_t child_pid = fork(); // 创建子进程

  if (child_pid > 0) {
    // 父进程
    sleep(60); // 父进程睡眠60秒,而不调用 wait()
    // 在这60秒内,子进程已经结束,会成为僵尸
  } else {
    // 子进程
    exit(0); // 子进程立即退出
  }
  return 0;
}

编译运行上述程序,在父进程睡眠的60秒内,用 ps aux | grep Z 命令就可以看到状态为 Z+ 的子进程。

验证 "Z" 状态

处于僵尸状态的进程的PCB需一直维护,而PCB本就是一个进程的 task_struct 的对象,这会一直占用内存空间 ------ 引发内存泄漏问题!

所以,不是只有 new/malloc 会引起内存泄漏僵尸进程也会引起内存泄漏 ------ 解决方法:通过 wait()_pid 的方式等待子进程并获取子进程退出结果

因此,父进程在未来除了获取子进程退出信息,还会进入等待模式将子进程的PCB释放掉 ------ 解决内存泄漏问题


2.5 如何清除僵尸进程?

  • 正确方法 :让父进程调用 wait()waitpid() 来回收子进程。
  • 常用方法 :如果父进程不回收,最直接的方法就是杀死父进程 。父进程被杀后,其所有子进程(包括僵尸进程)会变成"孤儿进程",被 init 进程(PID=1)收养。init 进程会定期调用 wait(),从而清理这些僵尸进程。
    • 命令:kill <父进程的PID>
  • 警告不要试图用 kill -9 杀死僵尸进程本身,因为它已经是死掉的进程,信号无法作用在它身上。

2.6 如何避免僵尸进程?

  • 在编写程序时,父进程应该使用 wait()waitpid() 系统调用来等待子进程结束。
  • 或者,父进程可以捕获 SIGCHLD 信号(当子进程状态改变时,内核会向父进程发送此信号),并在信号处理函数中调用 wait()

🌟 各位看官好我是工藤新一¹呀~

🌈 愿各位心中所想,终有所致!

相关推荐
Sadsvit8 小时前
Ansible: YAML语法与 Playbook写法解析
linux·运维·服务器·centos·自动化·ansible
滴滴滴嘟嘟嘟.8 小时前
嵌入式Linux驱动开发:ICM20608六轴传感器SPI驱动
linux·运维·驱动开发
Sadsvit9 小时前
Ansible变量的定义与使用
linux·运维·centos·自动化·ansible
特种加菲猫9 小时前
深入Linux内核:IPC资源管理揭秘
linux·笔记
北冥电磁电子智能10 小时前
江协科技STM32学习笔记补充之001。为什么C语言在对STM32编程过程中的二进制要用十六进制来进行读写。而不能直接用二进制来进行读写。
linux·stm32
工藤新一¹10 小时前
Linux 孤儿进程 (Orphan Process)
linux·孤儿进程·c/c++·orphan process
大白菜2015010510 小时前
ubuntu 创建系统服务 开机自启
linux·运维·ubuntu
lhxsir11 小时前
linux连接服务器sftp无法输入中文
linux·运维·服务器