【Linux详解】进程的状态 | 运行 阻塞 挂起 | 僵尸和孤儿状态

目录

操作系统中

运行状态

阻塞状态

进程状态转换

Linux系统中

查看进程状态

深度睡眠状态

[T 暂停状态](#T 暂停状态)

[Z 僵尸状态](#Z 僵尸状态)

孤儿状态

文章手稿


xmind:

引言

介绍系统中的进程状态及其管理方式。将通过结合操作系统原理和实际代码示例,详细说明进程的各种状态、转换过程以及处理方法。

操作系统中

一个进程通常有三种状态

  • 就绪状态(Ready):表示进程已经具备运行所需要的一切条件,只需要等待CPU的分配就可以运行。进程处于就绪状态时,通常会被添加到就绪队列,等待调度器分配CPU资源。
  • 运行状态(Running):表示进程正在被CPU执行。处于运行状态的进程正在使用CPU进行计算或其他操作。
  • 阻塞状态(Blocked):表示进程因为某些原因暂时无法继续执行,需要等待一些特定条件的解除之后才能继续运行。例如,当进程等待I/O操作完成或者等待某个资源可用时,会转入阻塞状态。进程在阻塞状态时,通常会被移动到阻塞队列中,等待条件的满足。

我们下面将对运行,阻塞,和阻塞挂起进行介绍~

运行状态

进程只要在运行队列中,就叫做 运行态

阻塞状态

关于进程:

① 一个进程使用资源的时候,可不仅仅是在申请 CPU 资源

② 进程可能会申请其它资源:磁盘、网卡、显卡,显示器资源......

如果我们申请 CPU 资源无法暂时无法得到满足,这就需要排队的 "运行队列" 。那么如果我们申请其他慢设备的资源呢?是需要排队的(task_struct 在进程排队)。

当访问某些资源(磁盘,网卡等),如果该资源暂时没有准备好,或者正在给其他进程提供服务,那么此时:

① 当前进程要从 runqueue 中逐出。

② 将当前进程放入对应设备的描述结构体中的waitqueue 。

进程状态:看PCB在哪个队列

内存不足了,操作系统就会把 该进程的代码和数据置换到磁盘上,进行 进程挂起

进程状态转换

进程状态的转换可以通过以下示例说明:

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

int main() {
    while (1) {
        printf("进程[%d]正在运行...\n", getpid());
        sleep(1); // 模拟阻塞状态
    }
    return 0;
}

通过运行上述代码并观察进程状态,可以理解进程在不同状态之间的转换过程。

三种状态的图示如下:


Linux系统中

进程状态用整数表示,这些整数存储在进程的task_struct结构体中。常见的进程状态包括:运行(R)、睡眠(S)、磁盘睡眠(D)、停止(T)、死亡(X)、僵尸(Z)和孤儿进程。

进程状态一览

状态代码 状态名称 描述
R 运行(Running) 进程正在运行或在运行队列中等待
S 睡眠(Sleeping) 进程在等待某事件完成,可被信号唤醒
D 磁盘睡眠(Disk Sleep) 进程在等待I/O操作完成,不可被信号唤醒
T 停止(Stopped) 进程被暂停,可通过信号恢复
X 死亡(Dead) 进程已终止,从进程列表中移除
Z 僵尸(Zombie) 进程已退出,父进程尚未读取其状态
孤儿(Orphan) 父进程已退出,被init进程收养

查看进程状态

使用ps auxps axj命令可以查看系统中进程的状态。例如:

ps aux
ps axj

这些命令输出的状态字段展示了进程当前的状态。

背后的原因让人暖心,cpu太快了,print显示器等待的时间在他看来就是在sleep了

深度睡眠状态

这个D状态我们就不模拟了......可能会把我的机子磁盘打满(害怕.dog)


T 暂停状态

比如看视频,听音乐,下载,都会有暂停。当你点击暂停的时候下载对应的代码就不跑了,此时这个进程你就可以认为是暂停状态。

再比如说我们调试程序,让程序打断点之后让程序运行起来,程序在打断点处停住的时候是将进程暂停了,所以你在gdb 调试或在 VS 下调试时你会发现程序会停下来,这就是暂停。

是进程挂起的一种。

我们可以先来看一下kill

接下来可以来尝试一下;

$ kill -19 4026,就会发现

gdb下的暂停状态,测试一下

$ gdb process  # 进入gdb调试
(gdb) l        # 查看代码
(gdb) b 9      # 打断点
q + 回车       # 退出

Z 僵尸状态

**僵尸状态:**当一个 Linux 中的进程退出的时候,一般不会直接进入 X 状态(死亡,资源可以立马被回收),而是进入 Z 状态。

为什么呢~

进程为 Z 状态,就是为了维护退出信息,可以让父进程或者 OS 读取记录的,退出信息会写入 test_struct。

以下是创建僵尸进程的代码示例:

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

int main() {
    pid_t id = fork();
    if (id < 0) {
        perror("fork");
        return 1;
    } else if (id == 0) { // 子进程
        printf("子进程[%d]开始运行...\n", getpid());
        sleep(5);
        printf("子进程[%d]退出...\n", getpid());
        exit(0);
    } else { // 父进程
        printf("父进程[%d]正在睡眠...\n", getpid());
        sleep(30); // 父进程延迟回收子进程
    }
    return 0;
}

我们可以写一个监控脚本来看一下~

while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1; echo "######" ; done

在另一个终端中运行ps命令可以看到子进程进入僵尸状态。

僵尸进程的危害

僵尸进程虽然不再运行,但它们仍然占用系统资源(如进程控制块task_struct)。如果父进程不及时回收子进程,会导致系统资源浪费,甚至内存泄漏。

避免僵尸进程

可以通过以下方式:

  1. 父进程及时调用wait()waitpid()回收子进程。
  2. 使用信号处理机制,在子进程退出时通知父进程进行回收。

孤儿状态

孤儿进程:父亲没了(bushi

即:父进程先退出了,子的父就变成1 号进程了,相当于被os领养了

测试一下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
 
int main(void) {
    pid_t id = fork();
    if (id == 0) {
        // child
        int cnt = 5;
        while (1) {  // 死循环,孩子进程就不退了
            printf("我是子进程,我还剩下 %ds\n", cnt--);
            sleep(1);
        }
        printf("我是子进程,我已经变僵尸了,等待被检测\n");
        exit(0);
    }
    else {
        // father
        int cnt = 3;
        while (cnt) {
            printf("我是父进程,我: %d\n", cnt--);
            sleep(1);
        }
        exit(0);
    }
}

监控一下:

 while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1;echo "######";done

我们可以top看一下1究竟是什么

❓ 疑问:父进程退出,为什么父进程没有变成僵尸?我们怎么没有看到父进程 为Z ?

  • 那是因为父进程的父进程是bash ,它会自动回收它的子进程,也就是这里的父进程。这里之所以没有看到父进程变成僵尸,是因为被 bash 回收了, z->x 的状态很快,所以你没看到。
  • 那为什么刚才我自己代码中的父进程创建的子进程,父进程没有回收子进程呢?那是因为你的代码压根就没有写回收,所以你的子进程就没有回收。

那我们怎么暂停呢,ctrl+c 只能干掉前台进程,

所以孤儿进程就要用到我们的杀进程:kill -9来暂停啦


文章手稿

相关推荐
cuisidong199715 分钟前
5G学习笔记三之物理层、数据链路层、RRC层协议
笔记·学习·5g
乌恩大侠16 分钟前
5G周边知识笔记
笔记·5g
wowocpp22 分钟前
ubuntu 22.04 硬件配置 查看 显卡
linux·运维·ubuntu
lucky九年26 分钟前
vscode翻译插件
ide·vscode·编辑器
山河君34 分钟前
ubuntu使用DeepSpeech进行语音识别(包含交叉编译)
linux·ubuntu·语音识别
鹏大师运维38 分钟前
【功能介绍】信创终端系统上各WPS版本的授权差异
linux·wps·授权·麒麟·国产操作系统·1024程序员节·统信uos
筱源源40 分钟前
Elasticsearch-linux环境部署
linux·elasticsearch
萨格拉斯救世主44 分钟前
jenkins使用slave节点进行node打包报错问题处理
运维·jenkins
帅得不敢出门44 分钟前
Gradle命令编译Android Studio工程项目并签名
android·ide·android studio·gradlew
川石课堂软件测试1 小时前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana