深入篇【Linux】学习必备:进程理解(从底层探究进程概念/进程创建/进程状态/进程优先级)

news2024/11/25 4:37:47

深入篇【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的存放位置,位置不同就表明优先级不同。

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

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

相关文章

不同路径——力扣62

文章目录 题目描述解法一 动态规划题目描述 解法一 动态规划 int uniquePaths(int m, int n) {vector<vector

【Java】项目管理工具Maven的安装与使用

文章目录 1. Maven概述2. Maven的下载与安装2.1 下载2.2 安装 3. Maven仓库配置3.1 修改本地仓库配置3.2 修改远程仓库配置3.3 修改后的settings.xml 4. 使用Maven创建项目4.1 手工创建Java项目4.2 原型创建Java项目4.3 原型创建Web项目 5. Tomcat启动Web项目5.1 使用Tomcat插件…

LeetCode150道面试经典题-- 两数之和(简单)

1.题目 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意…

pytest框架快速进阶篇-pytest前置和pytest后置,skipif跳过用例

一、Pytest的前置和后置方法 1.Pytest可以集成unittest实现前置和后置 importunittestimportpytestclassTestCase(unittest.TestCase):defsetUp(self)->None:print(unittest每个用例前置)deftearDown(self)->None:print(unittest每个用例后置)classmethoddefsetUpClass…

JDK17下载与安装(完整图文教程含安装包)

1.下载JDK17安装包 官网下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/ 同时提供一份网盘下载地址&#xff0c;大家按需自取&#xff1a;点击下载 JDK 所有版本的安装方法都一样&#xff0c;其他版本也不用重复找教程了。 网盘直接放了 JDK 6 – …

Python教程(8)——一文弄懂Python字符串操作(下)

Python字符串操作 字符串常用方法字符串更多方法介绍 字符串常用方法 字符串在编程中是一种不可或缺的数据类型&#xff0c;它在文本和字符数据时提供了丰富而强大的功能。掌握了字符串的使用方法&#xff0c;你能够更加便捷地进行文本处理、数据操作、用户交互等任务&#xf…

存储器分配算法

1.设计目的与要求 1.1设计目的 本设计的目的是使学生了解动态分区分配方式中使用的数据结构和分配算法&#xff0c;并进一步加深对动态分区存储管理方式及其实现过程的理解。 1.2设计要求 用C语言分别实现采用首次适应算法和最佳适应算法的动态分区分配过程malloc()和回收过程…

多表联合查询

1.创建student表 mysql> CREATE TABLE student ( -> id INT(10) NOT NULL UNIQUE PRIMARY KEY , -> name VARCHAR(20) NOT NULL , -> sex VARCHAR(4) , -> birth YEAR, -> department VARCHAR(20) , -> address VARCH…

【AWS 大赛】亚马逊云科技:2023 直冲云霄训练营入营考试报名与答题答案参考

目录 一、报名 &#xff08;1&#xff09;选择 “解决方案架构师-助理级” &#xff08;2&#xff09;未登录先注册账号 &#xff08;3&#xff09;登录 &#xff08;4&#xff09;报名 &#xff08;5&#xff09;报名成功 二、答题 &#xff08;1&#xff09;开始…

FreeRTOS(互斥信号量)

资料来源于硬件家园&#xff1a;资料汇总 - FreeRTOS实时操作系统课程(多任务管理) 目录 一、互斥信号量的定义与应用 1、互斥信号量的定义 2、互斥信号量的应用 3、简要了解递归互斥信号量 二、优先级翻转问题 1、运行条件 2、优先级翻转编程测试 三、互斥信号量的运…

[HDLBits] Exams/m2014 q3

Consider the function f shown in the Karnaugh map below. Implement this function. d is dont-care, which means you may choose to output whatever value is convenient. //empty

[HDLBits] Exams/2012 q1g

Consider the function f shown in the Karnaugh map below. Implement this function. (The original exam question asked for simplified SOP and POS forms of the function.) //

文本三剑客之grep命令和awk命令 1.0 版本

grep awk 1.grep命令1.1 基本格式1.2 常用选项 2.awk命令2.1 awk工作原理2.2 awk命令格式2.3 awk常用内置变量 1.grep命令 1.1 基本格式 grep [选项]… 查找条件 目标文件1.2 常用选项 选项功能 -m [ x ]匹配x次 后停止,x为具体数字-v取反 -i忽略字符大小写 -n显示匹配的 …

GrapeCity Documents for Excel, Java Edition Crack

GrapeCity Documents for Excel, Java Edition Crack 增加了对SpreadJS.sjs文件格式的支持&#xff1a; 更快地将大型Microsoft Excel文件转换为.sjs格式。 使用较小的占用空间保存导出的文件。 将Excel/SpreadJS功能导入SpreadJS/从SpreadJS导出。 从.sjs文件中压缩的JSON文件…

小程序发布注意事项

1、使用HBuildx的 发布 功能发布小程序&#xff0c;因为编译完的代码目录不是同一个 如果使用 运行 到小程序&#xff0c;最后发布的版本会显示”无法连接本地服务器“ 2、使用unicloud的云服务 uniCloud发行 | uni-app官网 阿里云的unicloud的话&#xff0c;使用request域名…

Docker启动、停止、删除容器的相关指令

关闭容器指令&#xff1a; docker stop name启动命令&#xff1a; docker start name删除容器&#xff1a; docker rm name 或 id查看所有容器id&#xff1a; docker ps -aq删除所有容器&#xff1a; docker rm docker ps -aq开启着的容器是不能被删除的。 查看容器信息&…

华为OD机试 - 最长连续子序列 - 双指针(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述备注 四、双指针1、双指针是什么&#xff1f;2、Java双指针算法适合解决哪些问题&#xff1f; 五、解题思路六、Java算法源码七、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专…

GUI、多线程编程、网络编程简介

GUI、多线程编程、网络编程简介 文章目录 GUI简介什么是GUIGUI有什么用使用方法 多线程编程什么是多线程编程多线程编程有什么用提高程序的响应能力提高程序的性能实现异步编程并发数据访问和共享资源实现复杂的算法和任务分解 进行多线程编程的步骤 网络编程什么是网络编程网络…

day6 STM32时钟与定时器

STM32时钟系统的概述 概念 时钟系统是由振荡器&#xff08;信号源&#xff09;、定时唤醒器、分频器等组成的电路。 常用的信号有晶体振荡器和RC振荡器。 意义 时钟是嵌入式系统的脉搏&#xff0c;处理器内核在时钟驱动下完成指令执行&#xff0c;状态变换等动作&#xff…

迪瑞克斯拉算法

迪锐克斯拉算法 简单来说就是在有向图中&#xff0c;给定一个图中具体的出发点&#xff0c;从这个点出发能够到达的所有的点&#xff0c;每个点的最短距离是多少。到不了的点&#xff0c;距离则是正无穷。有向&#xff0c;无负权重&#xff0c;可以有环。 所以说&#xff0c;迪…