慢吞云雾缓吐愁 发表于 2024-7-11 01:02:19

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

目次
        使用体系中
运行状态
阻塞状态
历程状态转换
 Linux体系中
检察历程状态
深度睡眠状态
T 停息状态
Z 僵尸状态
 孤儿状态
文章手稿
xmind: 
https://img-blog.csdnimg.cn/direct/1f59b5a308164fe9bd76dce01c79d4a1.png

文章手稿可见文末 
弁言

介绍体系中的历程状态及其管理方式。将通过结合使用体系原理和现实代码示例,详细阐明历程的各种状态、转换过程以及处理惩罚方法。


使用体系中

一个历程通常有三种状态
   

[*]就绪状态(Ready):表现历程已经具备运行所必要的齐备条件,只必要等待CPU的分配就可以运行。历程处于就绪状态时,通常会被添加到就绪队列,等待调治器分配CPU资源。
[*]运行状态(Running):表现历程正在被CPU实行。处于运行状态的历程正在使用CPU进行盘算或其他使用。
[*]阻塞状态(Blocked):表现历程由于某些缘故原由暂时无法继承实行,必要等待一些特定条件的排除之后才能继承运行。比方,当历程等待I/O使用完成大概等待某个资源可用时,会转入阻塞状态。历程在阻塞状态时,通常会被移动到阻塞队列中,等待条件的满足。
我们下面将对运行,阻塞,和阻塞挂起进行介绍~ 
https://img-blog.csdnimg.cn/direct/20b101d7c69e42da9719fe8b1e223d8c.png
运行状态

 历程只要在运行队列中,就叫做 运行态。
https://img-blog.csdnimg.cn/direct/352ed44838e24b6fb186260d41a02d76.png

阻塞状态

   关于历程:
① 一个历程使用资源的时候,可不但仅是在申请 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;
}
通过运行上述代码并观察历程状态,可以明白历程在不同状态之间的转换过程。
三种状态的图示如下:
https://img-blog.csdnimg.cn/direct/ea00c39278614bbdac38330e6cd334c3.png
 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 aux或ps axj命令可以检察体系中历程的状态。比方:
ps aux
ps axj
这些命令输出的状态字段展示了历程当前的状态。
https://img-blog.csdnimg.cn/direct/18ca74fdc239440aa28f21db19fb2025.png
背后的缘故原由让人暖心,cpu太快了,print表现器等待的时间在他看来就是在sleep了
https://img-blog.csdnimg.cn/direct/0cbfb6c3053249e3b240746aca36eb9e.png
深度睡眠状态

https://img-blog.csdnimg.cn/direct/9b660389ba864e73b628cf3407451c86.png
这个D状态我们就不模拟了……大概会把我的机子磁盘打满(害怕.dog) 
T 停息状态

比如看视频,听音乐,下载,都会有停息。当你点击停息的时候下载对应的代码就不跑了,此时这个历程你就可以以为是停息状态。
   再比如说我们调试程序,让程序打断点之后让程序运行起来,程序在打断点处停住的时候是将历程停息了,以是你在gdb 调试或在 VS 下调试时你会发现程序会停下来,这就是停息。
是历程挂起的一种。
我们可以先来看一下kill
https://img-blog.csdnimg.cn/direct/f4b1b5734cdb4bffb397d2a96014c582.png
接下来可以来尝试一下;
$ kill -19 4026,就会发现
https://img-blog.csdnimg.cn/direct/94b35b01e4884407afb0bd2d0cb71dba.png
gdb下的停息状态,测试一下
$ gdb process# 进入gdb调试
(gdb) l      # 查看代码
(gdb) b 9      # 打断点
q + 回车       # 退出 https://img-blog.csdnimg.cn/direct/dfab3656801744fdbbb44eed48289586.png
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 https://img-blog.csdnimg.cn/direct/8e98c1fe3fcf41fc849479061e2a6752.gif
在另一个终端中运行ps命令可以看到子历程进入僵尸状态。
https://img-blog.csdnimg.cn/direct/228c78faa9b64395a52871ac2852bd6c.png
僵尸历程的危害
僵尸历程虽然不再运行,但它们仍然占用体系资源(如历程控制块task_struct)。假如父历程不及时回收子历程,会导致体系资源浪费,甚至内存泄漏。
避免僵尸历程
可以通过以下方式:
   
[*]父历程及时调用wait()或waitpid()回收子历程。
[*]使用信号处理惩罚机制,在子历程退出时通知父历程进行回收。
 孤儿状态

孤儿历程:父亲没了(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
https://img-blog.csdnimg.cn/direct/3991fecdc9da486882e8d37d6e02999f.png
我们可以top看一下1究竟是什么
https://img-blog.csdnimg.cn/direct/2303e228577c487c9800e1a15254abf1.png
❓ 疑问:父历程退出,为什么父历程没有酿成僵尸?我们怎么没有看到父历程 为Z  ?
   

[*]那是由于父历程的父历程是bash ,它会自动回收它的子历程,也就是这里的父历程。这里之以是没有看到父历程酿成僵尸,是由于被 bash 回收了, z->x 的状态很快,以是你没看到。
[*]那为什么刚才我自己代码中的父历程创建的子历程,父历程没有回收子历程呢?那是由于你的代码压根就没有写回收,以是你的子历程就没有回收。
那我们怎么停息呢,ctrl+c 只能干掉前台历程,
以是孤儿历程就要用到我们的杀历程:kill -9来停息啦
https://img-blog.csdnimg.cn/direct/8822d2bc49a54611bbf1ef52469d564d.png
文章手稿

https://img-blog.csdnimg.cn/direct/0cd54acd881d48729a35d6237b3dc86f.jpeg
https://img-blog.csdnimg.cn/direct/4d8ba580852a4360ac93ee7833e9b4af.jpeg

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Linux详解】历程的状态 | 运行 阻塞 挂起 | 僵尸和孤儿状态