
🎬 个人主页:HABuo
📖 个人专栏:《C++系列》 《Linux系列》《数据结构》《C语言系列》《Python系列》《YOLO系列》
⛰️ 如果再也不能见到你,祝你早安,午安,晚安

目录
[📖1.1 什么是僵尸进程](#📖1.1 什么是僵尸进程)
[📖3.1 Linux下查看运行的进程信息](#📖3.1 Linux下查看运行的进程信息)
[📖3.2 进程优先级](#📖3.2 进程优先级)
前言 :
上篇博客我们详细地了解了部分进程状态,核心部分就在于如何理解一般操作系统层面的进程状态:运行、阻塞、挂起。接着我们在理解上述的基础上,我们认识了Linux下的进程状态都有哪些,今天这篇博客我们就继续来认识Linux系统下比较重要的两种进程状态:僵尸进程和孤儿进程 ,在博客的最后我们再来了解一下进程优先级的概念!
本章重点:
本篇文章着重讲解什么是僵尸进程?僵尸进程是怎样形成的?僵尸进程的危害以及孤儿进程的形成原因和OS的解决方法,最后讲解进程优先级相关内容,包括PRI和NI值得含义,Linux下的优先级范围。
📚一、僵尸进程
上篇博客我们见到了Linux下的进程状态,如下所示:


可以看到其中还有Z、X状态,其中Z状态就是僵死状态,我们也可以称之为僵尸进程。
📖1.1 什么是僵尸进程
问一个问题:一个进程在退出的时候,要不要让操作系统知道?我进程退出碍你操作系统什么事,错,你在学校里完成学业完成期末考试,放假回家,要不要让学校知道,这是当然的,不然你被骗子拐跑了,学校还不知道,学校是要担责的!因此一个进程要退出要让操作系统知道这个进程把任务完成的怎么样,也即是进程退出的时候不能立即释放进程对应的资源(PCB),要保存一段时间,让父进程/OS来进行读取!
在理解上述知识基础上,我们就可以下定义了:
僵尸进程 :子进程已结束,但父进程/OS未收集其退出状态,此时该进程就变成了僵尸进程
僵尸进程:当一个进程死亡的时候,它此时状态信息,要让父进程或者操作系统知道
(因为操作系统或者父进程要回收你)因此死亡进程的PCB仍然是保留在内存的,这类进程叫做僵尸进程,如果不释放就i会导致内存泄漏的问题
示例:
cpp
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id = fork();;
if(id == 0)
{
printf("我是一个子进程,pid:%d,ppid:%d\n", getpid(), getppid());
sleep(5);
exit(1);
}
else
{
while(1)
{
printf("我是一个父进程,pid:%d,ppid:%d\n", getpid(), getppid());
sleep(1);
}
}
return 0;
}

上面所使用的监控脚本如下:
cppwhile :; do ps axj | head -1 && ps axj | grep mytest2 | grep -v grep; sleep 1; done代码解释:我们让子进程5秒后自动退出,而父进程一直陷入循环中,不让它去回收子进程,此时子进程就进入了僵尸状态
僵尸进程的危害:
- 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。父进程如果一直不读取,那子进程就一直处于Z状态!
- 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护!PCB一直维护在内存,就会导致内存泄漏的问题!
①占用系统资源
-
占用进程表项(每个进程占用约1KB左右内存)
-
Linux有最大进程数限制
②可能导致的问题
-
系统无法创建新进程
-
进程ID耗尽
-
系统性能下降
📚二、孤儿进程
僵尸进程是父进程还在,子进程没了。那么孤儿进程顾名思义,父进程没了,子进程还在!处于孤儿进程的子进程就变成了后台进程,普通的ctrl+c无法进行退出,只能使用kill -9 进程标识符,将子进程杀死
-
定义:父进程先于子进程结束,子进程成为"孤儿"
-
处理机制:会被 init 进程(PID=1)收养
-
特点:无害,系统会自动处理
示例:
cpp
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
printf("我是一个进程 PID:%d,my PPID:%d\n",getpid(),getppid());
sleep(2);
pid_t id = fork();
if(id > 0)
{
int cnt=2;
while(cnt--)
{
printf("我是父进程 PID:%d,my PPID:%d\n",getpid(),getppid());
sleep(1);
}
exit(0);
}
else
{
while(1)
{
printf("我是子进程 PID:%d,my PPID:%d\n",getpid(),getppid());
sleep(1);
}
}
return 0;
}

从中得到以下几点:
- 进程变为孤儿进程后,进程便被操作系统领养(1号进程)(如果不领养变成僵尸进程就不会被回收了)
- 进程进入孤儿进程便变为了后台进程,无法ctrl+c进行停止
📚三、进程优先级
📖3.1 Linux下查看运行的进程信息
在Linux下,可以使用ps指令显示当前系统运行的进程信息,包含进程状态、资源使用情况等内容ps的-l选项可以显示长格式信息,包括F(标志)、S(状态)、UID、PID等详细字段:
bash
ps -l / ps -al
bash
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
| 字段 | 简称 | 全称 | 含义说明 |
|---|---|---|---|
| F | Flags | 进程标志 | 进程标志,十六进制,表示进程特性 |
| S | STAT | 进程状态 | 进程当前状态(最重要的字段之一) |
| UID | User ID | 用户ID | 进程所有者的用户ID |
| PID | Process ID | 进程ID | 进程的唯一标识符 |
| PPID | Parent Process ID | 父进程ID | 父进程的PID |
| C | CPU Utilization | CPU使用率 | 进程最近的CPU使用率 |
| PRI | Priority | 优先级 | 进程调度优先级(数字越小优先级越高) |
| NI | Nice Value | 谦让度值 | 用户可调整的优先级修正值(-20到19) |
| ADDR | Address | 内存地址 | 进程在内存中的地址(常显示为"-") |
| SZ | Size | 内存大小 | 进程占用的物理内存页数 |
| WCHAN | Waiting Channel | 等待通道 | 进程休眠的内核函数名称 |
| TTY | Teletypewriter | 控制终端 | 进程关联的终端设备 |
| TIME | CPU Time | CPU时间 | 进程累计使用CPU的时间 |
| CMD | Command | 命令 | 启动进程的命令名称 |
📖3.2 进程优先级

可以观察到进程信息中有两个PRI、NI两个信息,接下来让我们来了解一下这两个是什么鬼!相信优先级这个概念很好理解,因为资源就这么多,如果所有进程都来用当然不够,所以一定有个先后,这就是优先级的目的所在。首先基调:优先级了解即可
PRI(priority缩写):表示该进程被执行的优先级
NI(nice的缩写):表示该进程优先级的修正值,nice其取值范围是-20至19,一共40个级别。
即最终优先级:PRI(新) = PRI(旧,一般就是80)+ NI
PRI越小进程越快被执行,因此如果加入正数Nice值,会使进程优先级降低;加入负数Nice值,会使进程优先级提高。
注意:Nice并不是进程优先级,它们不是一个概念,可以理解成Nice值是进程优先级的修正数据。
更改nice值
可以使用 renice 命令更改已存在进程的nice值:
bash
renice <新nice值> -p <进程id>

📚四、总结
本篇博客我们又认识了Linux下的两种进程状态:僵尸进程和孤儿进程,在最后又介绍了进程优先级的概念。
小结一下:
僵尸进程:子进程退出但是父进程并没有进行回收
- 子进程的PCB还在内存中,会导致内存泄漏的问题
孤儿进程:父进程退出,子进程还在
- 孤儿进程会被操作系统(1号进程)所领养
- 自动变为后台进程,只能用kill -9杀死进程
| 特性 | 孤儿进程 | 僵尸进程 |
|---|---|---|
| 产生原因 | 父进程先结束 | 父进程未调用wait() |
| 父进程 | init进程(PID=1) | 原始父进程仍在 |
| 进程状态 | 正常运行或停止 | 已终止(Z状态) |
| 资源占用 | 正常占用 | 占用进程表项 |
| 危害性 | 无害 | 可能造成问题 |
| 处理方式 | 系统自动处理 | 需要手动清理 |
关于进程优先级的内容大家了解即可!
