进程概念与进程状态

目录

一、进程理解和进程控制块

进程理解

Linux中的进程

查看进程

1.ps ajx  查看所有的进程信息

2. /proc/目录查看

系统调用接口

getpid() 获取进程的pid

​编辑

getppid() 获取进程的父进程的pid

fork创建进程

fork用法:

fork原理理解:

二、进程状态

进程状态理解

操作系统进程状态分类

运行状态

阻塞状态

挂起状态

Linux进程状态与演示

R状态(运行状态)

S/S+状态(浅度睡眠状态, 属于阻塞状态)

D状态(深度睡眠状态, 属于阻塞状态)

T状态(stopped状态,属于阻塞状态)

​编辑

t状态(tracing stop状态,属于阻塞状态)

X状态(死亡状态)

Z状态(僵尸状态)

孤儿进程


一、进程理解和进程控制块

进程理解

课本观点: 内存中的程序/运行起来的程序

由冯诺依曼体系结构知,程序在运行之前先要加载到内存中

而在所有程序运行之前,OS是最先运行起来的,所以当其他程序加载到内存后,OS就要对进程做管理了,如何管理?先描述,再组织!描述就是形成结构体对象,里面包含被管理进程的各种属性,比如id,数据或代码地址等等), 组织就是用数据结构将结构体对象链接起来,此时对进程的管理就转化成了对链表的增删查改,所以当有1个新的程序加载到内存时,只需要在数据结构(比如链表)添加节点即可

上述描述进程的结构体对象叫做进程控制块(pcb)

进程定义: 进程 = 可执行程序 + 内核数据结构(pcb等)

进程 vs 程序

1.程序在磁盘中,进程在内存中

2.进程比程序多了内核级别的数据结构

3.进程是动态的,程序是静态的

动态的意思是一个进程可能不会一次运行完,所以可能运行了一部分,cpu又去调度其他进程了,此时该进程就处于等待状态,一会运行完其他进程,cpu又继续调度该进程,该进程又运行起来了,这就是动态的含义!

Linux中的进程

Linux中进程的pcb具体是struct task_struct{ }, pcb是所有操作系统的进程控制块的统称,而struct task_struct 是具体的操作系统---Linux的进程控制块

Linux中是用双链表把task_struct组织起来的

不要认为一个进程的pcb只能在一张链表里, 可能同时在多个数据结构中

查看进程

1.ps ajx  查看所有的进程信息

2. /proc/目录查看

/proc/是一个动态的目录,存放了所有存在的进程,目录的名称就是用这个进程的id命名的!

/proc/里面的数字就是进程对应的pid

当一个程序运行起来之后变成进程, /proc/目录下就会添加对应该进程的目录

当终止掉进程之后,发现/proc/下对应的进程的目录也就没有了

下图是查看/proc/目录下一个进程对应的目录所包含的文件,这些文件包含的是内存中的进程的属性信息

删除进程对应的可执行程序之后,进程是知道的!!

cwd表示当前工作目录,简称当前目录,所以之所以不带路径时创建的文件默认位置就是cwd,默认情况下,进程启动所在的目录就是当前目录,也就是cwd的内容

所以如果改变了cwd的内容,那么"当前目录"也就变了!

系统调用接口

getpid() 获取进程的pid

哪个进程调getpid()接口,就返回哪个进程的pid

获取进程的pid有何用??比如可以通过进程pid杀死进程

getppid() 获取进程的父进程的pid

每次重新运行,进程的pid都在变, 而ppid始终不变,ppid的意思是parent process id, 也就是父进程id的意思,而这个父进程是bash,也就是命令行解释器

fork创建进程

之前启动进程是用./启动,我们还可以通过代码来创建进程,fork()是一个系统调用,用来创建进程

fork用法:

1.fork()之前只存在1个父进程,fork()之前的代码只会被父进程执行一次,而fork()之后的代码会被父子进程都执行一次

2.fork返回值不唯一,给子进程返回0,给父进程返回子进程的pid

3.创建子进程肯定是为了让父进程和子进程做不一样的事情,且由于fork给父子进程的返回值不同,所以我们可以通过if/else分支语句分流来让父子进程执行不同的代码

fork原理理解:

1. fork()创建的子进程有自己的代码和数据吗?? fork()之后的代码父子进程都会执行,那么fork()之前的代码子进程会执行吗?? 不会执行的话子进程可以看到fork()之前的代码吗?

①fork()创建子进程,并没有自己的代码和数据,OS只会为子进程创建pcb, 以父进程为模板,把父进程的大部分属性数据拷贝给子进程pcb, 少部分会修改(比如pid, ppid), 所以fork()之后的代码父子进程都会执行

fork之前的代码和数据子进程也可以看到,但是程序在执行的有程序计数器(pc指针)会保存当前执行指令的下一条指令地址,当fork语句之前完毕后,程序计数器也来到了fork()下一行,所以子进程只会执行fork()之后的代码

2.fork给子进程返回0, 父进程返回子进程的id, 如何理解?

父进程:子进程 = 1 :n, 所以一个进程的父进程是唯一的,给子进程返回0就表示进程正常结束了,而1个父进程的子进程可能有多个,为了便于父进程为子进程的管理,所以要给父进程返回子进程的pid来唯一标识子进程

3.fork()之后,父子进程谁先运行??

父子进程谁先运行用户是不确定的,由各自PCB中的调度信息(时间片, 优先级等) + 调度器算法共同决定!再简单说,是由操作系统决定的!

4.如何理解fork()有两个返回值?一个函数为啥有两个返回值?

有两个返回值说明fork()函数里的return语句被执行了两次, 而一个函数要完成的核心工作都在return语句之前, 比如fork()函数要做的事情:找到父进程pcb, 为子进程pcb申请空间,以父进程pcb为模板创建子进程pcb, 让子进程pcb指向父进程的代码和数据,将子进程放入调度队列和父进程一样去排队,所以在return语句之前,子进程已经被创建好了,而一旦创建好了,父子进程就要执行一样的代码,return语句也是代码,所以子进程也要执行return语句,所以fork()函数会有两个返回值

5.如何理解同一个变量有不同的值??

---父子进程是独立的

杀死子进程后,父进程正常运行

杀死父进程后,子进程正常运行

可以看到,进程之间是独立的(包括父子进程),由于进程 = 代码/数据 + 内核数据结构,内核数据结构是独立的很好理解,因为父子进程都有各自独立的pcb, 代码和数据却是共享的,这如何保证独立性??

首先代码是只读的,不会修改,所以当父进程死亡后,OS发现子进程还在读取代码,所以不会回收代码,只会回收父进程pcb, 而数据是会被修改的,比如父进程是要依据变量是否为0来决定是否退出,而子进程就是要修改该变量的,这就无法独立了

所以结论就是代码是共享的,而数据父子进程必须各自私有一份,但并不是直接把父进程要用到的所以变量拷贝一份,否则可能造成空间浪费或创建子进程效率低下,OS是不会做低效的事情的,所以采用写时拷贝的方法,正常是共享同一份数据(浅拷贝),当子进程需要对数据做修改时,再拷贝需要修改的变量即可(深拷贝)

返回的本质是写入,id是父进程定义的变量,里面保存的是数据,所以返回的时候,发生了写时拷贝,所以一个变量有两个值,更深入的理解到进程地址空间再展开

同一个变量,同样的地址,但是有不同的内容,说明这里打印出的地址不是物理地址!

二、进程状态

进程状态理解

进程状态本质就是pcb中的一个字段/变量:int status, 所以我们可以通过宏定义将状态设置成不同的值来代表不同的状态, 例如:

#define NEW 1

#define RUNNING 2

#define BLOCK 3

所谓的进程状态变化就是修改整形变量的值(将pcb中的status设置成新值),例如:

pcb->status = NEW

然后就可以通过判断进程状态,将进程放入不同的队列当中, 例如:

if(pcb->status == NEW) pcb放入...队列

else if(pcb->status == RUNNING) pcb放入...队列

else pcb放入...队列

操作系统进程状态分类

运行状态

处于运行队列中的进程都是运行状态,不一定正在被cpu调度,而是"我已经准备好了,可以随时被cpu调度"

阻塞状态

当一个进程被调度器调度后放到cpu上去执行,在执行代码的时候或多或少代码中会有访问软硬件的操作,以访问硬件中的键盘为例,加入代码中有scanf/cin函数,等待用户去输入,但是用户就是不输入,此时键盘上没有数据,也就是键盘资源没有就绪,那么该进程就无法继续往下执行了!

而硬件资源没有就绪,操作系统一定是先知道的,因为操作系统要管理硬件资源,先描述再组织,用链表的形式将硬件设备对应的结构体变量链接起来,进行管理, 如下图所示,每种设备都有描述该设备属性的结构体对象,OS就将这些结构体变量链接起来,而每个结构体内部有1个成员变量是等待队列指针,用来链接因该设备资源没有准备就绪而导致处于阻塞状态的进程

进程状态改变的本质就是:

1.将进程pcb放入新的队列中(如运行队列或等待队列等)

2.改变pcb中的状态值(如将status修改成RUNNING或BLOCK)

把进程pcb从运行队列放入等待队列叫做该进程阻塞了,而把进程pcb从等待队列放入运行队列叫做该进程被唤醒了!

在用户层面,看到的进程阻塞的现象就是进程卡住了! 卡住的原因就是某种软硬件资源没有准备就绪,OS把进程pcb放入了等待队列并且把状态值修改了!所以我们看到程序一卡一卡的其实就是OS把进程pcb从等待队列到运行队列之间来回移动!

挂起状态

当一个进程被阻塞了,注定了硬件资源没有准备就绪,此时这个进程就无法被cpu调度,但是该进程的代码和数据都在内存中,但是cpu无法执行,如果此时内存资源已经严重不足,而OS是不允许任何低效或者浪费的行为发生的, 此时OS如何解决该问题?

因为该进程阻塞了,代码和数据无法执行或访问,所以OS会将当前的代码和数据交换到磁盘中,节省了一部分的内存资源,此时进程所处的状态就叫做挂起状态!

进程被挂起说明:

1.交换代码和数据是针对所有被阻塞的进程, OS会把所有设备的状态列表检测一遍,从而根据pcb中的status的状态找到被阻塞的进程代码和数据

2.将内存代码和数据交换到磁盘上,访问外设效率是很低,但是目前的主要矛盾就是OS内存资源不足,所以要先解决主要矛盾

3.置换代码和数据到磁盘的特定位置---swap分区(大小一般和内存大小一样或者是内存的2倍), 当底层资源就绪后,该进程又要被重新调度了,此时OS又会把swap分区的代码和数据重新加载到内存中

4.swap分区不宜过大,否则swap过大,导致swap很难被写满,OS一旦出现内存资源不足,就会很依赖swap分区,频繁的和swap分区进行代码和数据置换,导致整个系统的效率低下

5.我们此处说的挂起全称是阻塞挂起,也就是挂起的前提是进程阻塞了,所以OS在选择进程调度时优先选择阻塞的进程

6.还有一种挂起叫做就绪挂起,也就是进程没有正在运行就被挂起,这将会导致大量的进程被挂起,很多代码和数据都要被交换到内存中去,当OS要调度时,大量代码和数据又要加载到内存中,所以大量时间OS在和外设交互,这就要求OS在换入换出和效率之间取得平衡

Linux进程状态与演示

上述介绍的运行状态,阻塞状态和挂起状态不是具体的OS的状态,下面介绍Linux中的进程状态

R状态(运行状态)

R状态就是运行状态,表明该进程正在被调度

S/S+状态(浅度睡眠状态, 属于阻塞状态)

这个进程在运行,但是不是R状态,却是S+状态,对比处于R状态进程的代码,发现只多了一句printf语句,printf设计了访问硬件(显示器)的操作, 而cpu执行代码速度很快,OS快速的将进程pcb在运行队列和等待队列来回切换,显示器资源不一定准备就绪了,所以我们看到的进程状态大多是阻塞状态,偶尔会有运行状态

这个进程在运行期间,命令行bash无法运行,且ctrl+c可以终止该进程,这种进程叫做前台进程

下面是以后台方式运行进程,此时进程的状态就是S了,进程运行时命令行解释器也可以运行,因为是后台运行(就和你在windows上同时干多件事情,比如下载软件的同时看电影), ctrl+c也是终止不了的,此时可以 kill -9 进程id 来杀死进程

D状态(深度睡眠状态, 属于阻塞状态)

为了避免OS误杀系统内的进程,导致重要数据丢失,OS给进程设置了一种状态叫做深度睡眠状态,处于深度睡眠状态的进程OS也没有资格杀掉,所以深度睡眠就是为了防止进程向磁盘写入关键数据时而被杀掉导致数据丢失最后出问题;深度休眠状态用户一般见不到,要是用户见到了,说明几乎计算机快要挂掉了!

T状态(stopped状态,属于阻塞状态)

进程之所以会有暂停状态,是因为进程可能会做一些系统层面不允许的事情,比如说将来进程已经和显示器没有关系了,但是还强制向显示器写入,或者OS暂时不想让当前的进程访问某些资源,就会将进程设置成T状态!

进程原本处于S+状态,但是暂停之后处于T状态,而不是T+状态,因为本来就是前台进程,暂停之后需要在命令行上输入,所以系统将进程设置成了后台进程,否则无法继续输入

t状态(tracing stop状态,属于阻塞状态)

debug程序的时候,追踪进程,遇到断点,进程暂停了!

所以阻塞状态是某种资源没有就绪,可以是软件资源,可以是硬件资源,也可以是用户的指令,一个处于阻塞状态也可能是等待其他进程!

X状态(死亡状态)

死亡状态就是该进程运行完终止了或者被OS杀死了,该进程都进入死亡状态,而死亡状态是一个瞬时状态,一般很难查到!

Z状态(僵尸状态)

创建进程的目的就是为了让进程完成某项任务,所以当进程退出的时候,应该告诉父进程或者操作系统任务完成的如何了,如何告诉呢??

进程退出后应该把执行任务的相关情况写入到自己的pcb中(main函数的 return 语句就是在干这件事,0表示进程正常退出), 进程的代码和数据可以释放了,但是pcb还不能释放,因为父进程/OS还要去读取pcb的内容,要知道任务完成如何了以及进程异常退出的原因!

当进程的代码和数据已经释放但是pcb还没有释放,此时进程所处的状态就叫做僵尸状态!

当父进程/OS读取退出进程的pcb之后,pcb中的status状态就会被改成X状态,然后pcb被释放!

子进程已经退出,但是父进程没有释放,此时子进程的pcb就会一直占据内存,造成内存泄漏!所以解决办法就是回收子进程!

孤儿进程

如果父进程先退出了,父进程会处于僵尸状态吗?? 子进程又会如何呢??

可以看到,父进程退出后,并没有处于僵尸状态,因为父子关系是相对的,父进程也有它的父进程,就是命令行解释器,所以父进程退出后被命令行解释器进程给回收了,而子进程没有了父进程,PPID变成了1,表明被1号进程领养了,这种进程就叫做孤儿进程!

计算机启动后,最先跑起来的进程是0号进程,操作系统是1号进程,在操作系统加载到内存之后0号进程就释放了!

父进程先于子进程退出,子进程必须要被领养,否则系统内就会存在大量的孤儿进程,导致严重的内存泄漏!

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

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

相关文章

TensorFlow训练大模型做AI绘图,需要多少的GPU算力支撑

TensorFlow训练大模型做AI绘图,需要多少的GPU算力支撑!这个问题就涉及到了资金投资的额度了。众所周知,现在京东里面一个英伟达的显卡,按照RTX3090(24G显存-涡轮风扇)版本报价是7000-7500之间。如果你买一张这样的单卡…

十一、Qt自定义Widget组件、静态库与动态库

一、自定义Widget组件 1、自定义Widget组件 使用步骤采用提升法(promotion)重新定义paintEvent事件 2、实现程序 (1)创建项目,基于QWidget (2)添加类,为Widget组件提升类 #inclu…

day44 ● 完全背包● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ

完全背包和01背包问题唯一不同的地方就是&#xff0c;每种物品有无限件。 首先再回顾一下01背包的核心代码 for(int i 0; i < weight.size(); i) { // 遍历物品for(int j bagWeight; j > weight[i]; j--) { // 遍历背包容量dp[j] max(dp[j], dp[j - weight[i]] val…

(二十)devops持续集成开发——使用jenkins的docker插件完成docker项目的流水线发布

前言 本节内容主要介绍jenkins如何集成docker插件&#xff0c;完成docker项目的流水线发布&#xff0c;在前面的章节中我们也介绍过docker项目的发布&#xff0c;可直接通过shell命令调用本地的docker服务完成docker项目的发布&#xff0c;本节内容我们使用docker插件来完成do…

Open CASCADE学习|视图

目录 Mainwin.h Mainwin.cpp Mainwin.h ​#pragma once#include <QtWidgets/QMainWindow>#include "Displaywin.h"#include "OCC.h"class Mainwin : public QMainWindow{ Q_OBJECTpublic: Mainwin(QWidget* parent nullptr); ~Mainwin();​pri…

Stable Diffusion 绘画入门教程(webui)-ControlNet(Shuffle)

Shuffle(随机洗牌)&#xff0c;这个预处理器会把参考图的颜色打乱搅拌到一起&#xff0c;然后重新组合的方式重新生成一张图&#xff0c;可以想象出来这是一个整体风格控制的处理器。 那么问题来了&#xff0c;官方为啥会设计个这样的处理器呢&#xff0c;主要是给懒人用的&am…

idea生成WebServices接口

文章目录 idea生成WebServices接口1.创建接口2.生成wsdl文件3.在soapUI中&#xff0c;生成6个文件4.将生成的文件拷贝到工程中5.在service-config中注册服务 idea生成WebServices接口 1.创建接口 新建一个webServices工程&#xff0c;按照接口规范生成接口、请求类、响应类。…

低代码与大语言模型的探索实践

低代码系列文章&#xff1a; 可视化拖拽组件库一些技术要点原理分析可视化拖拽组件库一些技术要点原理分析&#xff08;二&#xff09;可视化拖拽组件库一些技术要点原理分析&#xff08;三&#xff09;可视化拖拽组件库一些技术要点原理分析&#xff08;四&#xff09;低代码…

《TCP/IP详解 卷一》第8章 ICMPv4和ICMPv6

目录 8.1 引言 8.1.1 在IPv4和IPv6中的封装 8.2 ICMP 报文 8.2.1 ICMPv4 报文 8.2.2 ICMPv6 报文 8.2.3 处理ICMP报文 8.3 ICMP差错报文 8.3.1 扩展的ICMP和多部报文 8.3.2 目的不可达和数据包太大 8.3.3 重定向 8.3.4 ICMP 超时 8.3.5 参数问题 8.4 ICMP查询/信息…

Mysql主从GTID与binlog

GTID与binlog **MySQL GTID&#xff08;Global Transaction Identifier&#xff09;和binlog&#xff08;二进制日志&#xff09;是用于搭建主从复制的两种不同的机制。** **GTID是MySQL 5.6版本引入的一种全局事务标识符&#xff0c;用于跟踪和标识复制过程中的事务。每个事务…

【玩转pandas系列】pandas数据结构—DataFrame

文章目录 前言一、DataFrame创建1.1 字典创建1.2 NumPy二维数组创建 二、DataFrame切片2.1 行切片2.2 列切片2.3 行列切片 三、DataFrame运算3.1 DataFrame和标量的运算3.2 DataFrame之间的运算3.3 Series和DataFrame之间的运算 四、DataFrame多层次索引4.1 多层次索引构造1.隐…

解决“设备未经过play保护认证”问题

第一步&#xff1a;获取GSF id 可以通过apkPure安装device id&#xff0c;复制GSF id&#xff0c;如下图 第二步&#xff1a;注册 打开网址&#xff1a;&#xff08;https://www.google.com/android/uncertified/?pli1 &#xff09;等待注册&#xff0c;很快就能完成, 第三步…

js 实战小案例

实战 时间 js 格式化时间 <script type"text/javascript">function formatDate(date) { let year date.getFullYear(); let month String(date.getMonth() 1).padStart(2, 0); // getMonth() 返回的月份是从0开始的&#xff0c;所以要加1&#xff0c;并…

如何快速起盘 七人拼团模式帮您解决 企业家可以过来看看!

七人团购模式&#xff0c;又称即拼模式&#xff0c;是一种创新的商业模式&#xff0c;通过结合拼团与二二复制的玩法&#xff0c;实现了快速的裂变和引流。该模式不仅具有完善的奖励激励制度&#xff0c;能够激发会员的推广积极性&#xff0c;还能显著提高平台产品的销量&#…

JavaWeb个人学习01

1:RequestParam(defaultValue "默认的值") 这个可以在一个参数的前面写上 要是前端不传值进来的话 这个形参就是你定义的默认值 2: slf4j 对应的是日志的输出 log.info("参数是 {}", detail); 3: 分页插件 PageHelper 用法: 准备工作: 引入依赖 …

mybatis 集成neo4j功能实现

文章目录 前言一、引入jar包依赖二、配置 application.properties三、Mybatis Neo4j分页插件四、Mybatis Neo4j自定义转换器handler五、MybatisNeo4j代码示例总结 前言 MyBatis是一个基于Java语言的持久层框架&#xff0c;它通过XML描述符或注解将对象与存储过程或SQL语句进行…

微信自动回复消息如何设置?演示如下

自动回复 当下&#xff0c;微信已经成为在工作中必不可少的沟通工具。但微信并没有自动回复的功能&#xff0c;一旦咨询量非常大&#xff0c;往往会出现回复不及时的情况。这样不仅会影响客户满意度&#xff0c;降低客户转化率&#xff0c;甚至会导致客户流失。 那如何实现自动…

Springboot教程(二)——过滤器、拦截器

过滤器 过滤器可以在调用控制器方法之前进行一些操作&#xff0c;过滤器类一般放在filter包下。 配置类注册 使用过滤器时&#xff0c;要实现Filter接口&#xff0c;并重写doFilter方法&#xff1a; class TestFilter : Filter {override fun doFilter(request: ServletReq…

怎么消除照片上的路人?分享几个无痕消除工具

穿梭于繁忙的街头&#xff0c;我们总会在不经意间捕捉到那些精彩的瞬间&#xff0c;但往往也会因为一些无关的路人破坏了整个画面的和谐。在摄影后期处理中&#xff0c;消除照片中的路人是一项常见的需求。现在&#xff0c;让我们一起探索如何巧妙地消除照片中的路人&#xff0…

Elasticsearch使用function_score查询酒店和排序

需求 基于用户地理位置&#xff0c;对酒店做简单的排序&#xff0c;非个性化的推荐。酒店评分包含以下&#xff1a; 酒店类型&#xff08;依赖用户历史订单数据&#xff09;&#xff1a;希望匹配出更加符合用户使用的酒店类型酒店评分&#xff1a;评分高的酒店用户体验感好ge…