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

打印 上一主题 下一主题

主题 692|帖子 692|积分 2076

目次
        使用体系中
运行状态
阻塞状态
历程状态转换
 Linux体系中
检察历程状态
深度睡眠状态
T 停息状态
Z 僵尸状态
 孤儿状态
文章手稿

xmind: 


文章手稿可见文末 
弁言

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


使用体系中

一个历程通常有三种状态
   

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

运行状态

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


阻塞状态

   关于历程:
  ① 一个历程使用资源的时候,可不但仅是在申请 CPU 资源
② 历程大概会申请其它资源:磁盘、网卡、显卡,表现器资源……
  
  假如我们申请 CPU 资源无法暂时无法得到满足,这就必要排队的 "运行队列" 。那么假如我们申请其他慢设备的资源呢?是必要排队的(task_struct 在历程排队)。
  当访问某些资源(磁盘,网卡等),假如该资源暂时没有准备好,大概正在给其他历程提供服务,那么此时:
① 当前历程要从 runqueue 中逐出。
② 将当前历程放入对应设备的形貌结构体中的waitqueue 。
历程状态:看PCB在哪个队列
内存不敷了,使用体系就会把 该历程的代码和数据置换到磁盘上,进行 历程挂起
历程状态转换

历程状态的转换可以通过以下示例阐明:
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main() {
  4.     while (1) {
  5.         printf("进程[%d]正在运行...\n", getpid());
  6.         sleep(1); // 模拟阻塞状态
  7.     }
  8.     return 0;
  9. }
复制代码
通过运行上述代码并观察历程状态,可以明白历程在不同状态之间的转换过程。
三种状态的图示如下:


 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命令可以检察体系中历程的状态。比方:
  1. ps aux
  2. ps axj
复制代码
这些命令输出的状态字段展示了历程当前的状态。

背后的缘故原由让人暖心,cpu太快了,print表现器等待的时间在他看来就是在sleep了

深度睡眠状态


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

T 停息状态

比如看视频,听音乐,下载,都会有停息。当你点击停息的时候下载对应的代码就不跑了,此时这个历程你就可以以为是停息状态。
   再比如说我们调试程序,让程序打断点之后让程序运行起来,程序在打断点处停住的时候是将历程停息了,以是你在gdb 调试或在 VS 下调试时你会发现程序会停下来,这就是停息。
  是历程挂起的一种。
  我们可以先来看一下kill

接下来可以来尝试一下;
$ kill -19 4026,就会发现

gdb下的停息状态,测试一下
  1. $ gdb process  # 进入gdb调试
  2. (gdb) l        # 查看代码
  3. (gdb) b 9      # 打断点
  4. q + 回车       # 退出
复制代码

Z 僵尸状态

僵尸状态:当一个 Linux 中的历程退出的时候,一般不会直接进入 X  状态(死亡,资源可以立马被回收),而是进入 Z 状态。
   为什么呢~
  历程为 Z 状态,就是为了维护退出信息,可以让父历程大概 OS 读取记录的,退出信息会写入 test_struct。
  以下是创建僵尸历程的代码示例:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. int main() {
  5.     pid_t id = fork();
  6.     if (id < 0) {
  7.         perror("fork");
  8.         return 1;
  9.     } else if (id == 0) { // 子进程
  10.         printf("子进程[%d]开始运行...\n", getpid());
  11.         sleep(5);
  12.         printf("子进程[%d]退出...\n", getpid());
  13.         exit(0);
  14.     } else { // 父进程
  15.         printf("父进程[%d]正在睡眠...\n", getpid());
  16.         sleep(30); // 父进程延迟回收子进程
  17.     }
  18.     return 0;
  19. }
复制代码
 我们可以写一个监控脚本来看一下~
  1. while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1; echo "######" ; done
复制代码

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

僵尸历程的危害
僵尸历程虽然不再运行,但它们仍然占用体系资源(如历程控制块task_struct)。假如父历程不及时回收子历程,会导致体系资源浪费,甚至内存泄漏。
避免僵尸历程
可以通过以下方式:
   

  • 父历程及时调用wait()或waitpid()回收子历程。
  • 使用信号处理惩罚机制,在子历程退出时通知父历程进行回收。
  
 孤儿状态

孤儿历程:父亲没了(bushi
即:父历程先退出了,子的父就酿成1 号历程了,相称于被os领养了
测试一下:
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. int main(void) {
  5.     pid_t id = fork();
  6.     if (id == 0) {
  7.         // child
  8.         int cnt = 5;
  9.         while (1) {  // 死循环,孩子进程就不退了
  10.             printf("我是子进程,我还剩下 %ds\n", cnt--);
  11.             sleep(1);
  12.         }
  13.         printf("我是子进程,我已经变僵尸了,等待被检测\n");
  14.         exit(0);
  15.     }
  16.     else {
  17.         // father
  18.         int cnt = 3;
  19.         while (cnt) {
  20.             printf("我是父进程,我: %d\n", cnt--);
  21.             sleep(1);
  22.         }
  23.         exit(0);
  24.     }
  25. }
复制代码
 监控一下:
  1. 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来停息啦


文章手稿




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

慢吞云雾缓吐愁

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表