【Linux进行时】进程状态

news2024/11/16 7:29:33

在这里插入图片描述

进程状态:

❓假设我们在上课,在B站上上课,请问我们的B站是不是一直运行呢?💡不是的!

❓假设我们同时打开了B站和PDF阅读器时,是怎么运行的呢?

💡每一个进程在CPU跑一会,再从CPU拿下来放上另外一个上去,周而复始,这种叫做分时操作系统

❓那就有一个问题,假设先用B站,为什么先将B站这个进程放上去,而不是PDF阅读器呢?

💡这就取决于进程状态!

思维导图:

image-20230915173545002

1.进程的状态

1.1三种基本运行状态

  • 运行状态

🔥R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里

❓一个进程只要把自己放到CPU上开始运行了,是不是一直要到执行完毕,才把自己放下来?

💡不是,每一个进程都有一个叫做:时间片的概念! 其时间大概是在10 ms左右。所以并不是一个进程一直在执行,而是这多个进程在一个时间段内所有代码都会被执行 —— 这就叫做【并发执行】

所以呢这就一定会存在大量的进程被CPU放上去、拿下来的动作 —— 这就叫做【进程切换】

  • 并发and并行

🔥并行:指的是两个或者多个事件同一时刻发生(可以类比为一个公路,三台车不同道同向行驶)

🔥并发:指的是一段时间内有多个程序同时运行

  • 就绪状态

🔥指的是进程已经处于准备好执行的状态,但是CPU没有空闲的,

  • 阻塞状态和挂起状态

有两个概念:阻塞和挂起!

阻塞:进程因为等待某种条件就绪 ,而导致的一种不推进的状态——进程卡住了——阻塞一定在等待某种资源

就相当于进程太多了,卡着一个进程,在这个进程来看就是在等待CPU调度(某种资源)

🔥阻塞:进程等待某种资源就绪的过程

❓为什么阻塞呢?

💡进程要通过等待的方式,等具体的资源被别人是用使用完之后,再被自己使用。

❓什么是资源呢?💡资源=磁盘或者网卡显卡等各种外设(这是硬件,软件也有)

因为操作系统是通过先描述,后组织的方式来进行管理,假设用struct来代表这些外设用链表连接起来,

❓可以存在大量的进程吗?💡可以的❓要管理吗?💡先描述再组织

假设我们有一个进程task_struct要加载到cpu中,它在等待一个网卡资源

image-20230810113159424

CPU说你不能在我上面跑了,所以task_struct就添加到这个队列的尾部等待网卡这个资源

image-20230810114050941

也就是我们的task_struct在我们的列入到某种资源的队列当中,它就不会被CPU调度了吗,这不就是卡住了吗,不就是阻塞了吗!!!!

🔥pcb可以被维护在不同的队列中的!

阻塞:阻塞就是不被调度——一定是因为当前进程需要等待某种资源就绪——一定是进程task_struct结构体需要再某种被OS管理的资源下排队

挂起:

image-20230810151426838

📚例子

假设task_struct是个下载任务,这里突然没网了,CPU说这个task_struct就不能跑了,task_struct就链接在网卡这个队列的中,这里就是阻塞了

操作系统通过自己的一套算法,把占有内存的,不被调度的进程的数据和代码交换到磁盘当中

磁盘那边放了一份交换后的代码和数据,这里的内存的代码和数据被释放,

在被再次调度前再把代码和数据换回来就可以了

image-20230810152157573

🔥其中代码和数据由操作系统暂时将我们的从内存换到磁盘的过程中,此时这个进程就是挂起状态,全称叫做阻塞挂起状态

2.Linux进程状态

task_struct是一个结构体,内部会包含各种属性,包括状态(status)

为了研究 Linux 进程状态,我们把源码先拿出来看看:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
  • R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的
    进程通常会等待IO的结束。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

用0表示R,用1表示S,用2表示D,用4表示T,用8表示t,用16表示X,用32表示Z

  • 运行状态

❓进程只要是在R状态,就一定是在CPU上运行吗?💡不一定!

image-20230810153626277

所有运行的进程只要在运行的队列中排队就可以了,所以CPU调度进程只要去我们的队列中去挑进程就可以了

❓进程是什么状态?💡一般也看这个进程在哪里排队

进程状态查看我们可以用ps

这里我们也能拿 ps 指令来看进程的状态,我们开小窗输入:

ps axj | head -1 && ps axj | grep process | grep -v grep

image-20230810164739743

显然这里S是休眠状态

🔥首先需要声明一点,状态后面跟加号,表示是一个 **前台进程,**你只需要知道的是,能够在键盘上 Ctrl+c 暂停的都可以叫前台进程。

其实我们CPU的运行速度都很快,我们看到这里它一直在打印,其实是显示器(外设)一直在打印,CPU一瞬间就完成了,所以状态是S

我们将test.c中printf注释掉,再试一下

image-20230810165142801

image-20230810165055831

我们发现这里变成了R状态了

所以这个进程不访问任何资源,只等你 CPU,只要你被运行期间不访问外设,就不会被阻塞。

不访问外设,那么死也会在等待队列里,一直在等待队列中,这就让 process 达成 R状态!

🔥这里printf的本质就是向外设(网卡、显示器)打印消息,进程只要是R状态,并不直接代表进程正在运行,而代表该进程在运行队列中排队

image-20230915091533355

🔥队列由操作系统自己维护

  • 睡眠状态

S状态——休眠状态,可中断休眠,它一直在等待某种资源,其实就是阻塞状态

image-20230915093710034

🔥我们一般把 状态叫作 浅度睡眠,也叫做 可中断睡眠

  • 顾名思义,当进程处于 S 状态,它可以随时被唤醒。
  • 不仅仅是操作系统可以唤醒,你也可以唤醒,甚至你想杀掉它都行。
$ kill -9 [pid]

我们用另外一个例子来更直观感受S状态

#include <stdio.h>
 #include <unistd.h>
 int main(void)
 {
   int a=0;
  printf("输入a的值:");
  scanf("%d",&a);
  printf("a=%d\n",a);                                        
  return 0;                                                                          
 }   

image-20230915095029316

将该进程运行起来我们可以看到其是出于S+的状态,因为【shell】此时正在等待用户的输入,这个就对应到了我们上面所讲到的 阻塞状态

🔥不仅如此,像我们一直在使用的bash,也可以算是一种【阻塞状态】,一直等待着我们去输入命令行,一旦有了的话它就进行读取

image-20230915095247760

  • 深度睡眠模式

这也是一种阻塞模式

"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */

这里D模式比S模式多一个disk,磁盘?那肯定跟磁盘有关系

一般而言,在 Linux 中,如果我们等待的是磁盘资源,我们进程阻塞所处的状态就是 D 状态。

与S状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号。
绝大多数情况下,进程处在睡眠状态时,总是应该能够响应异步信号的。否则你将惊奇的发现,kill -9竟然杀不死一个正在睡眠的进程了!于是我们也很好理解,为什么ps命令看到的进程几乎不会出现D状态,而总是S状态。

🔥 而TASK_UNINTERRUPTIBLE状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了。

  • 暂停状态

T状态,暂停状态

我们先通过这个指令来了解一下,我们要使用的就是下面的18和19信号

kill -l

image-20230915110704336

进程暂停与进程休眠(阻塞) 没有关系,只是单纯不想让这个进程跑了。

比如有些进程在执行任务时,用户想让这个进程暂停一下,这其实很好理解。

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

kill -19 进程pid    暂停该进程
kill -18 进程pid    解除暂停

image-20230915111618657

kill -18 pid解除一下

  • 死亡状态

X状态,死亡状态

kill -9 PID

dead 代表死亡,所以 X状态对应的就是 死亡状态

这个没有什么好说的,X 状态的进程就代表死亡了,可以随时等待 OS 来收尸了。

  • 僵尸状态

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

❓当进程退出后不直接变成X状态而是变成Z状态这是为什么呢?

💡当一个进程退出的时候,那最关心它的便是【父进程】。因为这个父进程费了很大的劲才将这个子进程fork出来,此时呢它突然挂掉了,那么此时父进程就必须去关心一下对应的子进程退出时的原因

在这个退出过程中,进程占有的所有资源将被回收,除了task_struct结构(以及少数资源)以外。于是进程就只剩下task_struct这么个空壳,故称为僵尸。
之所以保留task_struct,是因为task_struc t里面保存了进程的退出码、以及一些统计信息。而其父进程很可能会关心这些信息。

我们来造一个僵尸出来看看:

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

image-20230915114258396

❓如果长时间处于僵尸状态,会有什么结果呢?

💡如果没有人收尸,该状态会一直维护,该进程的相关资源 (task_struct) 不会被释放!内存泄露!一般必须要求父进程进行回收,如何回收的问题我们会在进程控制章节讲解。

❓为什么要main()的返回值呢?💡return 0;这个是进程退出码

🔥如果-个进程退出了,立马X状态,立马退出,你作为父进程,有没有机会拿到退出结果呢? ? ? Linux当进程退出的时候,- -般进程不会立即彻底退出,而是要维持一个状态叫做Z, 也叫做僵尸状态— 方便后续父进程(OS)读取该子进程退出的退出结果!

只要父进程不退出,这个僵尸状态的子进程就一直存在。那么如果父进程退出了呢,谁又来给子进程“收尸”?
当进程退出的时候,会将它的所有子进程都托管给别的进程(使之成为别的进程的子进程)。托管给谁呢?可能是退出进程所在进程组的下一个进程(如果存在的话),或者是1号进程。所以每个进程、每时每刻都有父进程存在。除非它是1号进程。

1号进程,pid为1的进程,又称init进程。
linux系统启动后,第一个被创建的用户态进程就是init进程。它有两项使命:
1、执行系统初始化脚本,创建一系列的进程(它们都是init进程的子孙);
2、在一个死循环中等待其子进程的退出事件,并调用waitid系统调用来完成“收尸”工作;
init进程不会被暂停、也不会被杀死(这是由内核来保证的)。它在等待子进程退出的过程中处于S状态,“收尸”过程中则处于R状态。

  • 孤儿进程

如果父进程 一直存在,子进程退出了,父进程对子进程不管不顾,子进程退出后就要进入僵尸状态

如果有父子两个进程,因为一些问题父进程不存在了,子进程还没有退出呢?

myproc.c

 1 #include<stdio.h>
    2 #include<unistd.h>
    3 int main()
    4 {
    5   pid_t id=fork();
    6   if(id==0)
    7   {
    8     //child
    9     while(1)
   10     {
   11       printf("我是子进程:pid:%d,ppid:%d\n",getpid(),getppid());
   12       sleep(1);
   13     }
   14   }
   15   else
   16   {
   17     //parent
   18     int cnt=10;
   19     while(1)
   20     {
   21        printf("我是父进程:pid:%d,ppid:%d\n",getpid(),getppid());
   22        sleep(1);
   23        if(cnt--<=0)break;                                                                            
   24     }
   25   }
   26   return 0;
   27 }
              

make一下,没有问题

image-20230814144031956

这里的$@

和$^

$@代表冒号左侧的,红色的,目标文件

$^代表冒号右侧的,蓝色的,依赖文件

image-20230814144445334

while :;do ps ajx | head -1&&ps ajx |grep myproc | grep -v grep;sleep 1;echo "----------";done

image-20230814150123577

我们在旁边把程序运行起来

我们发现我们的父进程是5388 ppid 7666

子进程 5389 ppid5388

image-20230814150923186

后面父进程中止了,只剩下子进程

子进程是5389 ppid变成了1

image-20230814151026354

父进程这时候退出了那是不是应该处于僵尸状态呢?

这里我们为什么没有看到父进程处于僵尸状态呢?

我们在命令行中启动一个进程时,它的父进程是bash

父进程退出之后,它的父进程是bash(命令行解释器),所以这个父进程退出的时候,它的父亲帮他回收了

相当于是我死了,我爹bash帮我回收了

我们发现这个子进程的父进程死掉后,它给自己又找了个爹(1号进程),1好进程可以叫为操作系统

image-20230814152258685

image-20230814152355978

如果不领养会发生什么呢?

如果有一天这个子进程挂了,并且他没有父进程,他就会被操作系统遗忘,就一直处于一种占用操作系统内存空间的情况,也就是内存泄漏

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1019086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Layui快速入门之第十三节 日期与时间选择器

目录 一&#xff1a;基本用法 API 渲染 属性 弹出提示 2.8 获取实例 2.8 解除实例绑定 2.8 关闭日期面板 2.7 获取某月的最后一天 二&#xff1a;常规用法 三&#xff1a;多类型选择器 四&#xff1a;范围选择 五&#xff1a;直接静态显示 六&#xff1a;更多功能…

Vue入门--vue的生命周期

一.什么是Vue 二.Vue的简介 官方网址 特点 三. 前后端的分离 重大问题 优势 4.Vue入门 定义一个管理边界 ​编辑 测试结果 vue的优势 ​编辑 测试结果 5.Vue的生命周期 vue的生命周期图 ​编辑建立一个html 测试结果 一.什么是Vue Vue是一种流行的JavaScript前端框…

华为云云耀云服务器L实例评测|使用宝塔10分钟部署一个围猫猫小游戏

目录 前言一、选择华为云云耀云服务器L实例的原因二、华为云云耀云服务器的优势三、快速部署一个小游戏&#xff08;1&#xff09;终端部署1、使用Termius工具连接终端2、安装Nginx3、上传打包文件 &#xff08;2&#xff09;宝塔可视化面板部署1、进入宝塔2、宝塔菜单3、上传代…

【实训项目】你好,教练-校园私教平台的设计与开发

1.设计摘要 随着社会的进步&#xff0c;人们的健康意识逐渐提高&#xff0c;越来越多的人选择在闲暇时间健身&#xff0c;在大学生群体中&#xff0c;这一现象犹为明显。在大学城内&#xff0c;有多家健身房供同学选择&#xff0c;也有许多同学选择在操场或者宿舍内自己健身&a…

使用Seata实现分布式事务

Seata 一&#xff1a;故事背景二&#xff1a;使用方法2.1 下载安装Seata2.4 修改对应配置文件。2.4.1 配置中心2.4.1 注册中心2.4.2 日志保存模式 2.3 启动Seata2.4 项目中集成2.5 数据库内新建undo_log 表进行日志记录2.6 编写代码测试Seata提供的分布式事务功能 三&#xff1…

带你熟练使用list

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;强烈推荐优质专栏: &#x1f354;&#x1f35f;&#x1f32f;C的世界(持续更新中) &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;…

EdgeX Foundry MQTT设备服务

一、部署edgex 1.运行命令行&#xff0c;进入放置docker-compose-fuji-no-secty.yml文件的路径下 下载到本地的docker-compose文件 执行&#xff1a; docker-compose -f docker-compose-fuji-no-secty.yml pull 拉取相关镜像&#xff0c;这里默认将文件中没有注释的服务镜像全部…

企业注册版 :ag-Grid Enterprise 30.1.0

Ag-Grid 被描述为一种商业产品&#xff0c;已根据 EULA 进行分发&#xff0c;并受到我们提供的技术团队的鼓励。它非常高级&#xff0c;具有与行分组一样的性能以及范围选择、主数据和案例、行的服务器端模型等等。Ag-Grid Enterprise 通常附带集成图表&#xff0c;允许用户和开…

无涯教程-JavaScript - IFS函数

描述 IFS函数检查是否满足一个或多个条件,并返回与第一个TRUE条件相对应的值。此功能已在Excel 2016中添加。 语法 IFS (logical_test1, value_if_true1, [logical_test2, value_if_true2], [logical_test3, value_if_true3]…) 争论 Argument描述Required/Optionallogical…

无涯教程-JavaScript - ATAN函数

描述 The ATAN function returns the arctangent, or inverse tangent, of a number. The returned angle is given in radians between -π/2 and π/2. The arctangent is the angle whose tangent is number. 语法 ATAN (number)争论 Argument描述Required/OptionalNumb…

springboot配置注入增强(二)属性注入的原理

一 原理 1 配置的存储 springboot在启动的时候会后构建一个org.springframework.core.env.Environment类型的对象&#xff0c;这个对象就是用于存储配置&#xff0c;如图springboot会在启动的最开始创建一个Environment对象 这个webApplicationType的枚举是在new SpringAppli…

Learn Prompt-Midjourney 图片生成

简介 随着 ChatGPT 的爆火&#xff0c;越来越多的人开始关注并尝试 AI 相关的应用。而图片生成就是其中一个备受瞩目的领域。目前已经有许多图像生成工具&#xff0c;如 Midjourney&#xff0c;Stable Diffusion&#xff0c;DALL-E&#xff0c;Firefly等。本课程主要是以 Midj…

Python 变量的类型注解

视频版教程 Python3零基础7天入门实战视频教程 类型注解 前面有个示例&#xff0c;我们函数参数是字符串类型&#xff0c;但是pycharm工具不知道&#xff0c;所以不会给我们提示s的方法&#xff0c;我们只能人工手写&#xff0c;效率大大降低。 类型注解&#xff1a; 在代码中…

心法利器[101] | 从大模型到大模型系统

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2022年新一版的文章合集已经发布&#xff0c;累计已经60w字了&#xff0c;获取方式看这里&…

使用ElementPlus实现内嵌表格和内嵌分页

前言 有时遇到这样的需求&#xff0c;就是在表格里面嵌入一个表格&#xff0c;以及要求带有分页&#xff0c;这样在ElementPlus中很好实现。以下使用Vue2语法实现一个简单例子&#xff0c;毕竟Vue3兼容Vue2语法&#xff0c;若想要Vue3版本例子&#xff0c;简单改改就OK了。 一…

数据结构——图的应用

文章目录 前言一、图的应用1. 最小生成树普里姆&#xff08;Prim&#xff09;算法克鲁斯卡尔&#xff08;Kruskal&#xff09;算法 2. 最短路径Dijkstra算法求单源最短路径 3. 拓扑结构4. 关键路径 总结 前言 图的应用 1.1 最小生成树 1.2 最短路径 1.3 拓扑结构 1.4 关键路径…

【网络教程】揭秘Windows SSH服务端免密登录:告别繁琐,享受安全连接

文章目录 开启Windows下的SSH服务端图形界面安装手动下载安装Windows如何查看系统用户名Windows如何查看本机IP开启免密登录Window生成秘钥Linux下生成秘钥配置公钥视频讲解开启Windows下的SSH服务端 这篇文章演示的环境是Windows11Windows的SSH服务端默认情况下是没有安装的,…

9.项目细节调整

文章目录 学习资料项目整体细节调整处理首页 学习资料 https://www.bilibili.com/video/BV13g411Y7GS/?p24&spm_id_frompageDriver&vd_sourceed09a620bf87401694f763818a31c91e 项目整体细节调整 处理首页 默认的首页 肯定不能给人看。文件在这个位置。 可以删除…

live555 BasicUsageEnvironment目录解读

文章目录 BasicUsageEnvironmentBasicHashTable.cppBasicTaskScheduler.cppBasicTaskScheduler0.cppBasicUsageEnvironment.cppBasicUsageEnvironment0.cppDelayQueue.cpp后续会进行更新 BasicUsageEnvironment ├── BasicHashTable.cpp ├── BasicTaskScheduler0.cpp ├─…

类加载器 - 双亲委派模型

文章目录 回顾一下类加载过程类加载器类加载器介绍类加载器加载规则类加载器总结自定义类加载器 双亲委派模型双亲委派模型介绍双亲委派模型的执行流程双亲委派模型的好处打破双亲委派模型方法 本文参考&#xff1a; 类加载器详解&#xff08;重点&#xff09; | JavaGuide(Ja…