深入篇【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…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...


