当前位置: 首页 > news >正文

【Linux】进程状态(二)

目录

前言:

一、进程状态:

1.运行状态(时间片)

2.阻塞状态

3.阻塞挂起状态

二、Linux进程状态:

1.运行状态(R)和阻塞状态(S) 

2.深度睡眠状态(D)

3.停止状态(T)

3.1使进程在后台运行 

4.追踪暂停状态(t)

5.死亡状态(X)和僵尸状态(Z)

5.1进程退出信息

三、孤儿进程:

四、命令总结:

总结:


前言:

我们已经知道了进程的一些属性,和如何创建子进程,那么接下来我们需要了解更多关于进程的概念。

一、进程状态:

我们主要讲述Linux的进程状态。

 我们先来了解什么是并行和并发。

并发是指多个任务或者事件在一段时间内交替执行。它强调的是在宏观上看起来这些任务是同时在进行,但在微观层面,实际上在某个瞬间可能只有一个任务在执行。还是用交通来类比,在一个单车道的道路上,有多辆车(任务)需要通过。由于只有一个车道,车辆不能同时通过,但是通过合理的调度,让每辆车都有机会前进,在一段时间内,看起来所有的车辆都在前进。例如,在一个单核处理器的计算机系统中,同时运行多个程序。由于只有一个核心,这些程序不能真正同时执行。操作系统会在这些程序之间快速切换,在一段时间内,每个程序都能得到执行时间,就好像它们在同时运行一样。

并行是指多个任务或者多个事件在同一时刻同时执行。这就好比有多个车道的高速公路,不同的车辆(任务)可以在不同的车道上同时前进,它们在物理时间上是重叠的。例如,在一个拥有多核处理器的计算机系统中,不同的核心可以同时处理不同的计算任务。假设我们有一个四核处理器,当执行四个独立的计算任务(如对四个不同的数据集进行数学运算)时,这四个任务可以同时在四个核心上运行,它们真正地在同一时间点都在执行,这就是并行处理。

Linux/Windows民用级别的操作系统,都是分时操作系统(调度追求公平)。

实时操作系统,实时操作系统一般要将一个特定的进程彻底执行完(在特定的领域会使用),不追求调度公平。

操作系统内会提供一个runqueue的运行队列:

而我们一般就是并发,也就是这样:

1.运行状态(时间片)

所以我们在理解运行状态时,就是当进程在运行队列中,该进程就处于运行状态。

此时CPU调度进程时,直接在runqueue队列中拿到一个进程的PCB即可。之后在CPU上运行完以后(也就是该进程的时间片到达以后),直接根据FIFO算法(先进先出)将时间片到达的进程再尾插到进程链表最后即可。

2.阻塞状态

操作系统有时如何管理硬件的呢?——先描述,在组织!

struct device
int type;
//比如1代表键盘
int status;//状态
//管理时间
//其他属性
}struct device
{int type;//比如1代表键盘int status;//状态//管理时间//其他属性
}

此时如果有代码执行到了scanf时,CPU不可能一直让其在CPU上等待。其实struct device结构体中还有一个属性就是task struct* wait_queue:

所以当代码运行到scanf时,用户没有输入时,就会把该进程放入该设备的wait_queue中等待。当用户输入数据后,会再把该进程链入到runqueue中。

当进程在键盘的等待队列中,也被称为阻塞状态。所以进程都是在队列中的,状态只是看在那些队列中而已。

运行和阻塞的本质就是让不同的进程处在不同的队列中。等待的本质:链入目标外部设备,CPU不调度。

3.阻塞挂起状态

因为即使在等待队列中,也会占据内存,可此时内存已经严重不足了。此时操作系统为了自身的安全,会把该进程代码和数据换出到磁盘中,当然PCB还保留着。对于阻塞的进程,此时该用户又输入了内容,但是阻塞状态都是运行状态给定的,所以会直接把该进程再链入到运行状态的队列中,此过程为换入。

磁盘中有一个专门为此存在的分区——swap分区。当有很多这样处于阻塞的进程,操作系统内存严重不足时,可能会把所有处于阻塞状态的进程都换出到swap分区上。

以上是阻塞挂起状态。当然还有运行挂起,也就是正在运行时的进程加载到swap分区中。但是这个风险较大,一般操作系统不会开启。 

这个过程就会变慢,也就是时间换空间。swap分区一般设计为内存的等量大小,根据工程师分配。

一般云服务器swap功能会被禁用,系统一般对时间要求更高。如果此时操作系统内存快要占满,就会杀死正在运行的进程以保证自身安全,这也就是我们有时可能会遇到的闪退。

二、Linux进程状态:

我们之前说的都是进程的状态,但是接下来我们要说的是Linux进程的状态:

1.运行状态(R)和阻塞状态(S) 

我们写一个代码:

#include<stdio.h>int main()
{int cnt = 0;while(1) {printf("hello world, cnt: %d\n", cnt++);}return 0;
}

运行该进程,在另外一个窗口中查看该进程的运行状态:

此时可以看到我们有时能查到S状态,有时能查到R状态(后面的+是指的在前台下跑的)。这是为什么? 

其实S状态就是阻塞状态。你可能不相信,此时我们把代码更改一下:

#include<stdio.h>int main()
{int cnt = 0;while(1) {scanf("%d", &cnt);printf("hello world, cnt: %d\n", cnt);}return 0;
}

此时我们再修改代码,此时我们先把打印语句给注释掉再次观察状态:

此时为运行状态。 这是为什么?

一个进程的时间片是非常短的,而print是往显示器中写的,也就是IO,所以大部分时间都是阻塞状态,之后偶尔是R状态。

加入你使用的是云服务器,所以还要从你服务器所在地传入到你所在的地方,也就是网络IO,所以你可能会很难查到R状态。

在阻塞状态的进程,可以被kill发送信号给杀掉。这种可中断睡眠也称为浅睡眠。

2.深度睡眠状态(D)

阻塞等待状态的一种,不可中断睡眠,深度睡眠。

此时有10w条银行数据,需要往磁盘中写入,进程A会处于阻塞状态,知道在磁盘中写完这些数据。此时内存资源严重不足了,操作系统把这个进程A给干掉了。此时磁盘在写入第8w条数据时出现了错误,于是向进程A汇报错误,结果进程A已经挂掉了,于是磁盘就把这些数据清空了。

此时就出现了问题,因为这10w条数据很重要,于是操作系统就把向磁盘中写入数据进程状态的阻塞又分出了一种D状态,也就是不可终止的深度睡眠状态。

这里不做演示。当在公司中看到该状态,有两种可能:

  • 磁盘可能要挂掉了,磁盘空间不足或者磁盘老化了
  • 操作系统可能要挂掉了

3.停止状态(T)

我们再次修改code.c代码,让其死循环打印:

这里相当于发送暂停信号。 之后再对其发送-18SIGCONT继续信号:

可以看到刚才停止的程序又继续运行了。这时我们在程序运行的Xshell窗口上Ctrl+C发现无法终止程序,你可能也发现了,当我们查看code这个运行程序时,发现状态栏后面的+消失了,之前说过,这里的+代表在前台运行,此时已经转到后台运行了,而Ctrl+C只能杀死前台运行的程序,所以此时只能通过信号终止进程。

发送-9信号杀死code程序。

3.1使进程在后台运行 

我们当然也可以让进程在后台进行。先将code.c代码修改为每隔一秒打印一次:

在调用程序时在后面加上&即可让程序在后台运行, 也就是Windows上的最小化。但是此时还是在前台打印了,因为我们并没用和终端文件做脱离,如果脱离就看不到了。

T状态一般是进程做了非法但不致命的操作,被OS暂停了。

4.追踪暂停状态(t)

我们需要使用gdb来观察t状态,我们更改makefile,生成一个debug版本的可执行程序。

为了方便动态查询,我们编写一个简单的shell脚本,这里死循环每隔一秒打印关于code进程的信息。

while :; do ps ajx | head -1 && ps ajx | grep code | grep -v grep;sleep 1; done

此时运行代码,会在断点位置停下:

所以t(tracing stop追踪的暂停)状态的本质就是当前进程被暂停了。此时我们n执行一步程序。 

可以看到由t状态变为S+状态。 当进程被追踪的时候,断点停下,进程状态就是t。

5.死亡状态(X)和僵尸状态(Z)

dead状态和Z(zombie)状态。

我们首先要知道,进程为什么要被创建出来?进程创建出来是为了完成用户任务的。但是进程结束,任务到底完成没有?这是通过进程执行的结果告知父进程/操作系统的。

5.1进程退出信息

我们这里来了解一个命令:$?

代表上一个进程退出时的退出信息,一般0代表程序正常退出,也就是完成任务;而其他就是任务就是出错的。

我们平时写的.c文件,在结尾都会有return,我们修改code.c代码,让其正常执行完并使用echo $?来查看退出结果(更加具体的细节会在环境变量和进程替换中讲解)。

关于X(dead)和Z(zombie)状态,我们先举一个例子:

有一个大爷,跑的很快,忽然倒下了。你在旁边经过,作为新时代的三好少年,你不会袖手旁观,于是你拨打了110,之后警察封锁了现场,法医进行了鉴定,之后对外宣布大爷是如何死亡的。这里我们就可以把大爷理解为进程,其状态如下:

对于Linux为什么有Z状态,因为我们要维持退出信息,方便父进程和操作系统来进行查询。 

当一个进程退出的时候:

1.代码不会执行---首先可以立即释放进程对应的程序信息数据
2.进程退出,要有退出信息(进程的退出码)保存在tastk_struct(int exit_code)内部
3.管理结构task_struct必须被OS维护起来,方便用户未进行获取进程退出的信息。

所以一个进程在创建的时候第一步是先创建内核数据结构(struct_task),之后加载代码和结构。在销毁的时候先释放代码和结构,之后OS维护内核数据结构,最后根据情况释放

接下来就用代码证明,我们先创建子进程,此时父子同时存在,之后让子进程退出,父进程什么都不做。

#include<stdio.h>
#include<unistd.h>int main()
{printf("父进程运行,pid: %d, ppid: %d\n\n",getpid(), getppid());pid_t id = fork();if (id == 0) {//子进程int cnt = 5;while(cnt){printf("我是子进程,我的pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt);sleep(2);cnt--;}}else {//父进程while(1){printf("我是父进程,我的pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);}}return 0;
}

我们再次开启一个窗口,持续观察进程和进程状态,还是使用这个代码进行观察:

while :; do ps ajx | head -1 ; ps ajx | grep myprocess | grep -v grep; sleep 1; done

defunct : 失灵的,不再使用的,死的 

当然我们还可以使用另外一种方法观察僵尸状态,我们修改myprocess.c代码,将子进程代码修改为死循环:

我们再次新开一个窗口,发送信号将子进程杀掉:

僵尸状态的进程,也称僵尸进程,维护自己的task_struct,方便父进程读取状态。也就是说,没人管它,它就是一直僵尸,task_struct也就一直占用内存资源,即使对应的代码和数据已经释放。此时存在的问题就是内存泄漏。

我们以前一般写的程序存在内存泄露我们一般发不现问题。是因为我们很快就把程序执行完了,但是在一个大项目中,不会很快就执行完程序,所以我们要避免内存泄漏。

最后,我们要回收这个进程,需要父进程读取子进程信息,子进程才会自动退出,此时task_struct也就会释放。

此时我们已经将子进程杀掉了,再次发送信号是无法杀掉的,因为你无法杀掉一个在概念上已经死掉的进程。

如何回收?我们后面再讲,不过这里先抛砖引玉一下,wait方法回收(在2号系统调用手册中)。

X状态是瞬时状态,我们捕捉不到,但是确实存在。

三、孤儿进程:

我们已经知道了僵尸进程,也就是父在子退;而还有另一种情况,就是父退子在。我们还用刚才的代码进行演示,这次我们将父进程杀死。

我们可以看到子进程成为了后端进程,所以先发送信号将其杀死。

这就奇怪了,在之前我们把子进程杀掉,其会变成僵尸状态;而我们把父进程杀掉他却没有变成僵尸状态,这是为什么?因为父进程的父进程是bash,当我们杀死父进程时,bash会自动回收。而子进程没有了父亲,PID为1的进程会将其领养。

接下来我们看看PID为1的进程时谁。执行top命令,我们可以先将其理解为任务资源管理器。

因为操作体统必须对所有的进程进行管理,当一个子进程没有父亲时,系统进程就会将其领养。 

四、命令总结:

top命令:相当于任务资源管理器。

可执行程序 + &:该进程在后台运行。

echo $?:查看上次进程退出信息。

死循环查看进程信息脚本:

while :; do ps ajx | head -1 && ps ajx | grep code | grep -v grep;sleep 1; done

总结:

我们知道了进程的状态并且知道如何观察,但是这只是冰山一角,我们目前只是初窥门径,欲知后事如何,且听下回分解(记得追剧啊!)。

相关文章:

【Linux】进程状态(二)

目录 前言&#xff1a; 一、进程状态&#xff1a; 1.运行状态(时间片) 2.阻塞状态 3.阻塞挂起状态 二、Linux进程状态&#xff1a; 1.运行状态(R)和阻塞状态(S) 2.深度睡眠状态(D) 3.停止状态(T) 3.1使进程在后台运行 4.追踪暂停状态(t) 5.死亡状态(X)和僵尸状态…...

domain 网络安全 网络安全域

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 文章目录 1、域的概述 1.1、工作组与域1.2、域的特点1.3、域的组成1.4、域的部署概述1.5、活动目录1.6、组策略GPO 2、域的部署实验 2.1、建立局域网&#xf…...

链表和STL —— list 【复习笔记】

1. 链表 1.1 链表的定义和类型 和顺序表一样&#xff0c;链表也是一种线性表&#xff0c;线性表存储结构为链式存储就是链表 链式存储不仅要保存数据元素&#xff0c;还要保存数据元素间的关系&#xff0c;这两个部分信息形成了结点。结点有两个域&#xff1a;数据域&#x…...

Java Map实现类面试题

Java Map实现类面试题 HashMap Q1: HashMap的实现原理是什么&#xff1f; HashMap基于哈希表实现&#xff0c;使用数组链表红黑树&#xff08;Java 8&#xff09;的数据结构。 public class HashMapPrincipleExample {// 模拟HashMap的基本结构public class SimpleHashMap&…...

技术架构和工程架构区别

技术架构 技术架构‌是对某一技术问题解决方案的结构化描述&#xff0c;包括组件结构及其交互关系。它涵盖部署方案、存储方案、缓存方案、日志方案等多个方面&#xff0c;旨在通过组织人员和技术&#xff0c;以最低的成本满足需求和应对变化&#xff0c;保障软件的稳定高效运…...

简单介绍JVM

1.什么是JVM&#xff1f; JVM就是Java虚拟机【Java Virtual Machine】&#xff0c;简称JVM。主要部分包括类加载子系统&#xff0c;运行时数据区&#xff0c;执行引擎&#xff0c;本地方法库等&#xff0c;接下来我们一一介绍 2.类加载子系统 JVM中运行的就是我们日常写的JA…...

纷析云:赋能企业财务数字化转型的开源解决方案

在企业数字化转型的浪潮中&#xff0c;财务管理的高效与安全成为关键。纷析云凭借其开源、安全、灵活的财务软件解决方案&#xff0c;为企业提供了一条理想的转型路径。 一、开源的力量&#xff1a;自主、安全、高效 纷析云的核心优势在于其100%开源的财务软件源码。这意味着…...

DeepSeek开源周第二弹:DeepEP如何用RDMA+FP8让MoE模型飞起来?

一、引言&#xff1a;MoE模型的通信瓶颈与DeepEP的诞生 在混合专家&#xff08;MoE&#xff09;模型训练中&#xff0c;专家间的全对全&#xff08;All-to-All&#xff09;通信成为性能瓶颈。传统方案在跨节点传输时带宽利用率不足50%&#xff0c;延迟高达300μs以上。DeepSee…...

NLP学习记录十:多头注意力

一、单头注意力 单头注意力的大致流程如下&#xff1a; ① 查询编码向量、键编码向量和值编码向量分别经过自己的全连接层&#xff08;Wq、Wk、Wv&#xff09;后得到查询Q、键K和值V&#xff1b; ② 查询Q和键K经过注意力评分函数&#xff08;如&#xff1a;缩放点积运算&am…...

【MySql】EXPLAIN执行计划全解析:15个字段深度解读与调优指南

文章目录 一、执行计划核心字段总览二、关键字段深度拆解1. type&#xff08;访问类型&#xff09;——查询性能的晴雨表典型场景分析&#xff1a; 2. key_len&#xff08;索引使用长度&#xff09;——索引利用率的检测仪计算示例&#xff1a; 3. Extra&#xff08;附加信息&a…...

论文笔记(七十二)Reward Centering(五)

Reward Centering&#xff08;五&#xff09; 文章概括摘要附录B 理论细节C 实验细节D 相关方法的联系 文章概括 引用&#xff1a; article{naik2024reward,title{Reward Centering},author{Naik, Abhishek and Wan, Yi and Tomar, Manan and Sutton, Richard S},journal{arX…...

Linux内核自定义协议族开发指南:理解net_device_ops、proto_ops与net_proto_family

在Linux内核中开发自定义协议族需要深入理解网络协议栈的分层模型。net_device_ops、proto_ops和net_proto_family是三个关键结构体,分别作用于不同的层次。本文将详细解析它们的作用、交互关系及实现方法,并提供一个完整的开发框架。 一、核心结构体的作用与层级关系 struct…...

SOME/IP-SD -- 协议英文原文讲解6

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.3.1 E…...

【数据处理】COCO 数据集掩码 Run-Length Encoding (RLE) 编码转二进制掩码

输入&#xff1a;结果.json 输出&#xff1a;mask.jpg json内容示例如下&#xff1a; {"labels":[ # class_id 1,2,3,...],"scores":[ # 置信度0.2,0.7,0.3,...],"bboxes":[[1244.0,161.0,1335.0,178.0],[1243.0,161.0,1336.0,178.0],[1242.0,1…...

Java中的缓存技术:Guava Cache vs Caffeine vs Redis

在Java中&#xff0c;缓存技术是提升应用性能的重要手段。常见的缓存技术包括Guava Cache、Caffeine和Redis。它们各有优缺点&#xff0c;适用于不同的场景。以下是对它们的详细对比&#xff1a; 1. Guava Cache 类型: 本地缓存 特点: 基于内存的缓存&#xff0c;适用于单机应…...

Day8 蓝桥杯acw讲解

首先先给大家看一道这个题&#xff0c; 我真的是太喜欢y总了&#xff0c;如果大家也是最近在准备蓝桥杯或者计算机相关的比赛&#xff0c;但是得加一个前提就是必须最好基础真的很好&#xff0c;要不然其实买了课&#xff0c;也没啥太大的用处&#xff0c;其实就可以以我本人举…...

《Operating System Concepts》阅读笔记:p147-p158

《Operating System Concepts》学习第 15 天&#xff0c;p147-p158 总结&#xff0c;总计 12 页。 一、技术总结 1.socket (1)定义 A socket is defined as an endpoint for communication(socket 是用于通信的端点&#xff0c;或者说socket 是通信端点的抽象表示。). A s…...

JSON Schema 入门指南:如何定义和验证 JSON 数据结构

文章目录 一、引言二、什么是 JSON Schema&#xff1f;三、JSON Schema 的基本结构3.1 基本关键字3.2 对象属性3.3 数组元素3.4 字符串约束3.5 数值约束 四、示例&#xff1a;定义一个简单的 JSON Schema五、使用 JSON Schema 进行验证六、实战效果6.1 如何使用 七、总结 一、引…...

java后端开发day20--面向对象进阶(一)--static继承

&#xff08;以下内容全部来自上述课程&#xff09; 1.static–静态–共享 static表示静态&#xff0c;是java中的一个修饰符&#xff0c;可以修饰成员方法&#xff0c;成员变量。 1.静态变量 被static修饰的成员变量&#xff0c;叫做静态变量。 特点&#xff1a; 被该类…...

FastJSON 默认行为:JSON.toJSONString 忽略 null 字段

完整的 FakeRegistrationController 代码&#xff0c;这让我可以全面分析后端逻辑&#xff0c;特别是为什么空的字段&#xff08;如 compareDate&#xff09;不返回给前端。我将详细分析代码的每个接口&#xff0c;尤其是与 list 请求和字段返回相关的部分&#xff0c;并解释原…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...