深入篇【Linux】学习必备:进程理解(从底层探究进程概念/进程创建/进程状态/进程优先级)
深入篇【Linux】学习必备:进程理解(从底层探究进程概念/进程创建/进程状态/进程优先级)
- 一.进程概念(PCB/task_struct)
- 二.查看进程(top/ps)
- 三.创建进程(fork)
- 四.进程状态(僵尸进程/孤儿进程)
- 五.进程优先级(PRI/NI)
一.进程概念(PCB/task_struct)
1.什么叫进程呢?
一个已经加载到内存中的程序就叫做进程(任务)。
2.对于一个进程,操作系统是如何管理的呢?
先描述,再组织
任何一个程序在加载到内存时,形成真正的进程是,操作系统要先创建描述进程属性的结构体对象,即PCB。
因为我们都是从属性来认识事物的本身的,当属性够多,这一堆属性的集合,就是目标对象。所以描述进程,当然是要描述这个进程的各种属性。
3.进程=内核数据结构(PCB)+自己写的代码和数据。
①进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。在OS操作系统下这个进程控制块我们称为PCB。
②而在Linux下我们称为task_struct。里面包含了进程的所有属性,最基本的组织进程task_struct方式是采用双向链表组织。
4.进程属性都有哪些呢?
PCB或task_struct结构体的内容是什么呢?我们可以将它们分类成下面一些:
①标识符:用来描述本进程的唯一标识符,可以区别其他进程。
②状态:进程的状态是如何,是在任务状态还是在退出状态。
③优先级:进程之间是有优先级的,用来竞争CPU。
④程序计数器:程序中即将被执行的下一条指令的地址。
⑤上下文数据:进程执行时处理器的寄存器中的数据。
⑥内存指针:包括程序代码和进程相关数据的指针。
⑦I/O状态信息:包含显式的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
5.进程被描述完后是如何组织起来的?
在内核源代码中可以找到它,所有运行在系统里的进程都以task_struct链表的形式存着内核中。
二.查看进程(top/ps)
1.进程的信息可以通过/proc系统文件来查看。(进程信息都放在/proc文件中)
2.查看进程还可以用top和ps这些指令获取。
比如查看test进程信息:
比如我创建一个程序test,运行起来后这个程序加载到内存中就变成进程了。所以我们就可以通过ps指令来查看这个进程的属性。
3.我们还可以通过系统调用来获取进程的信息,比如进程的标识符。
进程id(PID)
获取自己进程的pid可以使用系统调用函数getpid().
父进程id(PPID)
获取自己父进程的ppid可以使用系统调用函数getppid().
这个进程的pid是27734这个是没有问题的(上面是27733,下面是27734其实是一个进程因为我把这个进程中断了,然后又运行起来了,最后pid不一样了,所以我们可以看出来每次运行时pid可能不同,但ppid却相同),我们可以查看一下这个pid所对应的进程。不过我更想知道这个进程的父进程是谁呢?等会我们可以查看一下。
我们可以清楚的看到27734是对应着./proc这个程序的。而它的父进程是bash。
所以我们可以明白,这个进程是由bash创建的,我们写的程序都是由bash创建进程运行的。而bash只负责创建进程,而子进程坏不坏跟它没有关系,它是不管的,所有它的ppid不变。
三.创建进程(fork)
1.我们手动输入命令,让程序加载到内存变成进程。这个方式是由bash创建进程的。
2.我们还可以通过系统调用fork创建进程,即在程序运行时创建进程。而要使用fork()创建进程,需要理解以下几点。
fork()函数—>创建进程
特点:①有两个返回值。当进程创建成功后,会将进程pid返回给父进程,将0返回给子进程。②fork之后通常要用if进行分流。
1.为什么fork()要返回不同的返回值?
返回不同的值,是为了让父子进程执行不同的代码块。父进程为了取反不同发子进程,通过pid控制子进程。
2.为什么要创建子进程呢?
为了让父和子执行不同的事情!而实现这样的方式就是返回两个返回值。
3.一个函数是如何做到有两个返回值的呢?
fork函数的主要功能就是创建子进程,进程创建成功后,也就是系统中多了一个PCB。但这个PCB并没有数据和代码。所以系统会让这个子进程共享父进程的代码。理论上数据也要共享的,但是进程之间是相互独立的,各自互不影响,所以要求父子进程不能共享同一份数据。子进程是重新开辟一块空间,将父进程的数据拷贝一份的,但这样的做法有点低效,因为子进程并不一定全部访问父进程的数据,这样就会占用内存。所以这里采用的方法是写时拷贝(用多少拷贝多少)。就是当子进程想访问父进程某个具体数据时,那就临时拷贝出来。
而fork函数里,在返回返回值之前,子进程就已经创建好了,所以这个子进程是共享fork函数的代码。也就是父进程会返回一次,子进程也会返回一次,所以最后返回两次。
4.一个变量为什么可以存不同的值?
这个涉及程序地址空间。将返回值返回给数据这个过程是在写数据,而根据上面的子进程数据是重新开辟一块空间,临时拷贝的。
所以这时候将返回值返回到不同的内存空间,父进程访问的是老数据空间,子进程访问的是一个新的拷贝空间。
5.bash是如何创建子进程的?
在理解fork之后我们肯定可以想到,bash创建子进程肯定用到了fork函数。一个进程用来打印命令行,一个进程用来创建。
1 #include <stdio.h>2 #include <unistd.h>3 int main()4 {12 //fork如果创建成功,会将当前进程的pid传给父进程,将0传给子进程。如果失败 ,则传返回-1;13 printf("begin,我是一个进程,我的pid是%d,ppid是%d\n",getpid(),getppid()) ;14 pid_t id= fork();15 17 if(id==0)//子进程18 {19 while(1)20 {21 22 printf("我是子进程,我的pid是:%d,ppid是:%d\n",getpid(),getppid());23 sleep(1);24 } 25 }26 else if(id>0)//父进程27 {28 while(1)29 {30 printf("我是父进程,我的pid是:%d,ppid是:%d\n",getpid(),getppid());31 sleep(1); 32 }33 }34 else//创建失败35 {36 37 }38 return 0;39 }
我们可以发现父进程就是当前正在运行的进程。而当前正在运行的进程的父进程就是bash。
四.进程状态(僵尸进程/孤儿进程)
想要理解一个正在运行的进程是什么意思,我们首先要知道进程的不同状态。一个进程可以多个状态。
在操作系统学科中主要有运行状态,阻塞状态,挂起状态。
1.运行状态
通常来说,一个进程在CPU上使用资源叫做运行状态,而现实是操作系统中有很多想要运行的进程,这些进程用链表组织起来,而每个CPU都会维护一个叫做运行队列,当进程想要运行时就将自己链入到运行队列中即可。而在运行队列中的进程就可以是运行状态了。
那进程如果没有完成任务,会一直在CPU上运行吗?当然不会!每个进程都有一个叫做时间片。在运行一定时间后就会退出CPU,等下次再运行,这样就不会存着一个进程一直在CPU上跑了。也说明在一个时间段内,所有进程都会被执行一遍,这个行为叫做并发执行。
而这一过程,必定存着许多行为:把进程放上CPU,从CPU中放下进程,这一过程叫进程切换。
2.阻塞状态
一个进程在等待某种资源时就是在阻塞状态,比如当一个进程想要获取从键盘上输入的数据时,但键盘就不输入时,这个进程就得不到数据,就要一直等键盘输入数据,这时就是阻塞状态。操作系统在管理硬件资源时,也是采取先描述,再组织的方法,将每个硬件资源都描述成一个结构体对象。而且每个结构体对象里面都有一个等待队列。当一个进程在等待这个硬件资源时,就会将这个进程链入对应的等待队列中,当有多个进程等待时,就将这些进程都链入队列中,直到进程进入就绪状态才会将进程直接链入运行队列中。
3.挂起状态
要理解当一个进程处于阻塞状态时,它的数据和代码是没有使用的,但还是占用着内存资源,当操作系统内存资源严重不足时,操作系统会将处于阻塞状态的进程对应的代码和数据放入磁盘里,只保留着内核在等待队列中等待。这个进程就是挂起状态。当进程进入运行队列时,操作系统才考虑将对应的数据从磁盘中再换去到内存中。
各种进程状态的本质就是决定当前进程的PCB在哪个队列里排队!!!
以上都是操作系统学科关于进程状态的一些介绍理解,而下面则是Linux中进程状态是如何理解的。
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:运行状态,但并不意味着进程一定在运行中,它表明进程要么在运行中要么在运行队列里。
S:睡眠状态(浅度睡眠)意味着进程在等待着某件事完成。这个进程一定在等待某种资源。
D:休眠状态(深度睡眠)这个进程在等待过程中是不可以被调度的,不响应任何需求。让进程在等待磁盘写入完毕期间,这个进程是无法被杀掉的。在这个状态的进程通常会等待IO的结束。
T:停止状态,可以通过发送信号让进程停止即这个进程进入T状态,这个暂停的进程还可以通过发信号继续运行。
X:死亡状态。这个状态只是一个返回状态,不会任务列表中看到。
4.僵尸进程
【特点】:
当一个进程结束后并不会立刻销毁,而是会等待父进程来"访问"它,当父进程给出回应后这个进程才会真正的结束。
进程一般退出的时候,如果父进程没有生成回收子进程的信号,子进程就会一直让自己处于Z状态,即僵尸状态,进程的相关资源尤其是task_struct结构体就不能被释放。
所以当进程退出并且父进程没有读取到子进程退出的返回代码就会进入僵尸进程,僵尸进程会一终止状态保持在进程表中,并会一直等待父进程读取退出状态代码,这个状态会一直占用资源,会造成内存泄漏!
【危害】:
当父进程不读取进程的退出代码时,进程就要一直维持退出状态,而维持退出状态需要数据维持,而这个数据就在task_struct结构体对象里里,所以当进程退出状态一直维持,那么对象就无法被释放,那么就会一直占用内存资源,当父进程创建很多子进程,并且就不读取子进程的退出代码,就会造成大量的内存泄漏。
5.孤儿进程
当父进程提前退出,子进程想要退出时,肯定会进入Z状态,那这个子进程该怎么办呢?
当父进程比子进程先退出,则这个子进程就会变成孤儿进程,那这个子进程就无法退出了吗?并不是!
这个进程会被操作系统’‘领养’'即一号进程。最后这个孤儿进程会被操作系统回收。
五.进程优先级(PRI/NI)
输入命令 ps -l 后可以看到:
PRI代表着这个进程的优先级,其值越小,优先级越高,越先被执行。
NI代表这个进程nice值,nice是用来修正优先级的。
1.nice是如何修正优先级的呢?
PRI(new)=PRI(old)+nice. PRI(old)一直都是80.
2.nice值的取值范围是[-20,19]而进程的优先级范围就是[60,99],所以进程有40个优先级可以调度。
3.可以使用top命令来更改进程的优先级
输入top命令后再输入r,会要求你输入要修改进程的pid,输入完后,会要求你输入要修改的nice值是多少
4.操作系统是如何根据优先级来先后调用进程的呢?
我们知道CPU维护着一个叫运行队列的东西,运行队列中其实有两个指针数组,一个是run数组,一个是wait数组,run数组里放的是进程地址,数组开辟140个空间,其中前100个空间是给不同的进程使用,我们不用管,而后40个空间是用来存放进程地址。
数组的位置和进程的优先级是一一对应的,因为进程优先级有40种,这里也有40个空间,所以100位置上链入着优先级为60的进程地址,139位置上链入的是优先级为99的进程。
所以可以根据数组下标的不同,从上到下遍历出来的PCB就是根据优先级调度的进程。
而当有新的进程想要运行时,并不会再进入run数组里面的队列中,而是会进入wait数组的对应优先级的队列中。
运行队列里还有两个二级指针,一个是run一个是wait,run就一直指向要运行的数组,wait就一直指向新来的进程的数组。
当run中的进程都被调度完后,就会将run和wait交换。这样新来的进程又可以被调度了。
而操作系统是如何知道数组中进程是否被调度完呢?这个根据位图来实现的!
总结:
①在运行队列中有两个指针数组。
②在这个队列中的进程都是R状态。
③调度优先级的本质:把PCB链入到对应队列的子队列那一个下标中,调整优先级的本质就是调整PCB在这个数组中的顺序。
④所以在调整优先级的同时会改变PCB的存放位置,位置不同就表明优先级不同。
相关文章:

深入篇【Linux】学习必备:进程理解(从底层探究进程概念/进程创建/进程状态/进程优先级)
深入篇【Linux】学习必备:进程理解(从底层探究进程概念/进程创建/进程状态/进程优先级) 一.进程概念(PCB/task_struct)二.查看进程(top/ps)三.创建进程(fork)四.进程状态(僵尸进程/孤儿进程)五.进程优先级(PRI/NI) 一.进程概念(PCB/task_struct) 1.什么…...

Python 潮流周刊#15:如何分析 FastAPI 异步请求的性能?
你好,我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容,大部分为英文。标题取自其中一则分享,不代表全部内容都是该主题,特此声明。 本周刊精心筛选国内外的 250 信息源,为你挑选最值得分享的文章、教程、开源…...

基于Java+SpringBoot+Vue的网吧管理系统设计与实现(源码+LW+部署文档等)
博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…...

redis设置database 不生效剖析
设置database 不生效剖析 前言配置加载类问题commons-pool 对象池 主页传送门:📀 传送 前言 事情是这样的 今天在拉取了同事的代码做redis缓存设置的时候,发现即使已经设置了database, 但是存数据的时候还是用的默认0数据库。这引起了我的好…...

汽车及汽车零部件行业云MES解决方案
汽配行业现状: 随着经济全球化进程加快,一直走在智能化改造,数字化转型前沿的汽车行业企业,面临的信息化需求也日益增加,不管德系,美系还是日系供应链的各大厂商,均将企业信息化,数字…...
算法工程师-机器学习面试题总结(4)
深度学习 DNN 描述一下神经网络?推导反向传播公式? 神经网络(Neural Network)是一种模拟人脑神经系统的计算模型。它由许多节点(神经元)和连接它们的权重组成,这些节点和权重可以学习和调整&a…...

Linux学习之awk函数
awk里边的函数分为内置函数和自定义函数。 内置函数有下边的几种: 算术函数(arithmetic) 字符串函数(string) 输入/输出函数和通用函数(input/output, and general) 自定义函数格式如下…...

Redis的数据结构到底是一种什么样的结构?
有了上一篇NoSQL的基础,我们也都知道了Redis就是一种典型的NoSql,那我们就先简简单单的介绍一下Redis: Redis是什么? Redis(Remote Dictionary Server)是一个开源的使用ANSI C语言编写的高性能键值存储系统…...

eclipse 导入项目js报错问题
eclipse 导入项目后会出现项目中的js文件报错(红叉),如下图所示,有时候报错的文件很多,需要集中处理。 解决办法: 右键项目名称》Properties》MyEclipse》JavaScript》Include Path,在右侧选择“…...

《HeadFirst设计模式(第二版)》第七章代码——外观模式
代码文件目录: Subsystem: Amplifier package Chapter7_AdapterAndFacadePattern.FacadePattern.Subsystem;/*** Author 竹心* Date 2023/8/8**///扬声器 public class Amplifier {int volume 0;//音量public void on(){System.out.println("The amplifier …...
前端杂项-个人总结八股文的背诵方案
个人总结八股文的背诵方案 URL到显示网页的过程 浏览器解析URL,获取协议,主机名,端口号,路径等信息,并通过DNS查询将主机名转换为对应的IP地址浏览器与服务器建立TCP,进行三次握手。浏览器向服务器发送HT…...

利用 3D 地理空间数据实现Cesium的沉浸式环境
推荐:使用 NSDT场景编辑器 助你快速搭建可编辑的3D应用场景 为了将大量异构 3D 地理空间数据处理和分散到各行各业的地理空间应用程序和运行时引擎,Cesium 创建了 3D Tiles,这是一种用于高效流式传输和渲染大量异构数据集的开放标准。3D Tile…...

微服务——ES实现自动补全
效果展示 在搜索框根据拼音首字母进行提示 拼音分词器 和IK中文分词器一样的用法,按照下面的顺序执行。 # 进入容器内部 docker exec -it elasticsearch /bin/bash# 在线下载并安装 ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch…...
北斗+5G 织就精确定位的“天罗地网”
今年,邓中亮更忙了。 外部会议,内部讨论,课题研究,还有疫情困扰期间没能出的差铆足劲似的补上,一天里,从离开床和回到床中间的时间都被工作冠名了。 北京邮电大学教授邓中亮 忙碌的加速键在2020年按下暂停…...
Ansible Roles详解
Ansible 的角色(Roles)是一种组织和管理任务和变量的方法,可以帮助您更好地组织和重用 Ansible 代码。角色是一个可重用的、自包含的 Ansible 单元,它封装了一组任务和变量,可以在不同的剧本中轻松地重用。 角色的目录…...

微服务学习笔记-基本概念
微服务是一种经过良好架构设计的分布式架构方案。根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务。 微服务的架构特征: 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力&…...

Linux查看GPU显卡/CPU内存/硬盘信息
显卡信息命令/CPU内存/硬盘 1.显卡2、CPU内存3、硬盘 1.显卡 nvidia-smi nvidia-smi(显示一次当前GPU占用情况) nvidia-smi -l(每秒刷新一次并显示) watch -n 5 nvidia-smi (其中,5表示每隔6秒刷新一次终端…...
SQLAlchemy 入门:Python 中的 SQL 工具包和 ORM
SQLAlchemy 是 Python 中一款非常流行的数据库工具包,它对底层的数据库操作提供了高层次的抽象。在本篇文章中,我们将介绍 SQLAlchemy 的两个主要组成部分:SQL 工具包 (SQL Toolkit) 和对象关系映射器 (Object-Relational Mapper, ORM) 的基本…...
react Hook+antd封装一个优雅的弹窗组件
前言 在之前学vue2的时候封装过一个全局的弹窗组件,可以全局任意地方通过this调用,这次大创项目是用react技术栈,看了一下项目需求,突然发现弹窗还是比较多的,主要分为基础的弹窗以及form表单式的弹窗,如果…...

HICP学习--BGP综合小实验
需要完善 一、实验拓扑 二、实验需求 1、R2-7每台路由器均存在一个环回接口用于建立邻居,同时还存在一个环回来代表连接用户的接口;最终这些连接用户的接口网络需要可以和R1/8的环回通讯 2、AS2网段地址172.16.0.0/16 减路由条目数量 三、实验步骤 首先配置IP R…...
Android多媒体——音/视频数据播放(十八)
在媒体数据完成解码并准备好之后,播放流程便进入了最终的呈现阶段。为了确保音视频内容能够顺利输出,系统需要首先对相应的播放设备进行初始化。只有在设备初始化成功后,才能真正开始音视频的同步渲染与播放。这一过程不仅影响播放的启动速度,也直接关系到播放的稳定性和用…...

5. TypeScript 类型缩小
在 TypeScript 中,类型缩小(Narrowing)是指根据特定条件将变量的类型细化为更具体的过程。它帮助开发者编写更精确、更准确的代码,确保变量在运行时只以符合其类型的方式进行处理。 一、instanceof 缩小类型 TypeScript 中的 in…...
【大厂机试题+算法可视化】最长的指定瑕疵度的元音子串
题目 开头和结尾都是元音字母(aeiouAEIOU)的字符串为元音字符串,其中混杂的非元音字母数量为其瑕疵度。比如: “a” 、 “aa”是元音字符串,其瑕疵度都为0 “aiur”不是元音字符串(结尾不是元音字符) “…...
SQL 注入开放与修复
开发: SQL 注入是一种数据库攻击手段。攻击者通过向应用程序提交恶意代码来改变原 SQL 语句的含义, 进而执行任意 SQL 命令,达到入侵数据库乃至操作系统的目的。 例如:下面代码片段中,动态构造并执行了一个 SQ…...

黑马Javaweb Request和Response
一.介绍 在 Web 开发中,HttpServletRequest 和 HttpServletResponse 是两个非常重要的类,它们分别用于处理客户端的请求和服务器的响应。以下是它们的详细说明和使用方法: 1. HttpServletRequest HttpServletRequest 是一个接口࿰…...
【学习笔记】深入理解Java虚拟机学习笔记——第3章 垃圾收集器与内存分配策略
第3章 垃圾收集器与内存分配策略 3.1 概述 略 3.2 对象已死? “死去”即不可能以任何途径访问到 3.2.1 引用计数算法 每个对象维护一个计数器,引用即加1,引用失效便减1。 3.2.2 可达性分析算法(主流) 即根据GC…...

CoordConv: CNN坐标感知特征适应
传统卷积 vs CoordConv 详细对比 传统卷积对空间位置不敏感,CoordConv通过显式添加坐标信息解决这个问题在特征图中嵌入(x, y)坐标和可选的径向距离r使模型能够感知空间位置关系 1. 传统卷积的"空间位置不敏感"问题 传统卷积的特点: 输入: …...
MySQL 8.0 绿色版安装和配置过程
MySQL作为云计算时代,被广泛使用的一款数据库,他的安装方式有很多种,有yum安装、rpm安装、二进制文件安装,当然也有本文提到的绿色版安装,因绿色版与系统无关,且可快速复制生成,具有较强的优势。…...
强化学习入门:交叉熵方法数学推导
前言 最近想开一个关于强化学习专栏,因为DeepSeek-R1很火,但本人对于LLM连门都没入。因此,只是记录一些类似的读书笔记,内容不深,大多数只是一些概念的东西,数学公式也不会太多,还望读者多多指教…...

SIFT算法详细原理与应用
SIFT算法详细原理与应用 1 SIFT算法由来 1.1 什么是 SIFT? SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换),是一种用于图像特征检测和描述的经典算法。它通过提取图像中的局部关键点,…...