💖作者:小树苗渴望变成参天大树🎈
🎉作者宣言:认真写好每一篇博客💤
🎊作者gitee:gitee✨
💞作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法🎄
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!
前言
task_ struct内容分类
标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级
。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据。
I/O状态信息: 包括显示的I/O请求分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息
上面红色部分的属性再后面都会介绍到。
一、进程优先级
我先从下面两个问题带大家先了解进程的优先级:
是什么: 权限和优先级是一样吗??权限是能与不能,而优先级是在能的基础上,对于资源谁先访问谁后访问
为什么: 因为我们的cpu只有一个,每次只能运行一个进程,而不是每个进程都有一个cpu,所以cpu的资源是有限的,注定的了进程之间是相互竞争的(竞争性),所以操作系统为了保证良性竞争,确认每个进程的优先级,就相当于排队,不能乱按顺序去执行
这都是由调度器去做,所以好的调度算法才能保证更好的良性竞争,但调度算法不是特别好,如果有的进程长时间得不到cpu资源,导致该进程的代码长时间得不到推进----该进程的饥饿问题(在windows经常会出现这种情况,表现为程序卡死了,然后弹出来一个框未响应)
一会会大致介绍一下Linux中的调度是怎么去做的
1.1查看进程优先级
我们先来写一个程序:
#include<unistd.h>
#include<stdio.h>
int main()
{
while(1)
{
printf("我是一个进程\n");
sleep(1);
}
return 0;
}
我们使用ps -l
查看,如果你是在其他窗口查看时候ps -al
我们只需要看其中两个属性PRI和NI
,他两个代表优先级,其余的不重要,顶多带大家在了解一下UID,他是唯一标识用户的就是账号,使用ls -aln
去查看:
接下来博主在来好好介绍一下PRI和NI这两个属性
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值
PRI and NI
- PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
- 那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值,PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:
PRI(new)=PRI(old)+nice
,这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值,nice其取值范围是[-20,19],一共40个级别
按照上面说的我们的PRI值越小,优先级越高,那我们如果我们想要的进程优先级改的特别小,那cpu是不是就可以一直给我们的程序跑了,但Linux操作系统不允许这样做,这样就不能保证其他进程的公平性,但是万一有的时候必须修改一些优先级,所以才有有了我们的nice,给你一个范围,在这个范围里面可以随便去修改(但是不建议修改),我们看到我们一般程序的默认优先级是80,加上nice就是[60,99]
PRI vs NI
需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。
可以理解nice值是进程优先级的修正数据
接下来也是通过修改nice值来修改优先级
接下面我就尝试带大家来修改一下优先级:
用top命令更改已存在进程的nice:
top
进入top后按“r”–>输入进程PID–>输入nice值
top就像当任务管理器,使用普通用户去修改优先级是不行的,需要使用root用户
我们如果修改的nice值小于-20,也会变成-20来处理,修改的值大于19也会按照19来处理,第二次修改nice值,会从起始的默认值开始算,不会按照第一次修改的PRI开始算。
我们最好不要去修改我们的nice值,因为没必要,Linux上的调度已经比较公平了,接下来在来理解一下进程优先级的概念:
- cpu资源分配的先后顺序,就是指进程的优先权(priority)。
- 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可能改善系统性能。
- 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能
1.2怎么根据优先级去开展调度
我们调度的前提是在运行态的时候,时刻准备着被调度,所以肯定是在运行队列里面,接下来我们来看看运行队列是怎么去做的:
看到这里大家应该知道我们调度器大致是怎么去调度的并且根据优先级去优先调度的,此时还有一个小问题就是怎么判断数组遍历结束,是不是就是判断数组是不是为空,此时就需要使用位图的知识,这里简单提一下,就是每个数组下标对应一个比特位,当有元素,比特位为1,不然就为0,所以我们再runqueue队列里面应该还有一个属性叫
bitmap[40]
,存放比特位的,当位图的值为0就说明数组为空了,可以进行交换了。这种方法也叫我们Linux上的大o(1)调度算法
还有其他概念:
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
前面两个概念相信大家已经有所了解,第三个并行,就是再多个物理cpu上跑的进程叫做并行,我们现在使用的电脑几乎都是只有一个cpu,而且并行很好理解,接下来我们就好好的理解一下并发
1.3进程的并发是怎么做到的??
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
我们再上一篇介绍运行态的时候画的图上说过什么是并发,大致意思就是再运行队列的进程会不止一次的从cpu拿上拿下(时间片的概念),就会导致运行队列上的进程会在一定的时间内将运行队列的所有进程都跑上一遍,把这个过程叫做并行。这就叫基于时间片基于进程切换的调度算法
接下会带大家理解计算机是怎么做到跑一次进程下次再跑,从上次结束的位置继续运行的??(进程切换是怎么做的)
小故事时间:
故事一、
我们再大学的时候不知道大家有没有听到过部队到学校里面征兵,而你是一个梦想当兵的人,由于当年没有考好导致上一个普通的学校,这次不对征兵你势必要去当兵,刚好不对也选中了你,至此你高兴的来到了宿舍和自己的室友说,我去当兵了,明年再见,什么都没干,就直接跟部队走了,结果你一年之后就回来了,然后准备完成剩下来的学业,结果你发现你早就被学校开除了,而且对应的宿舍已经不是你的了。你惊讶说到,我是去当兵了,不是退学了,结果学校领导跟你说,你走的时候又没有告诉我你去当兵,我们发现你每次上课和考试都不来,直接给你开除,你后悔的拍了拍大腿。但事实已经改变不了了。
故事二、
还是相同的场景,部队来征兵,此时的你学聪明了,没有直接走,你是先回宿舍将自己的宿舍里面的东西收拾好,然后去辅导员哪里跟学校说明了情况,学校将你的血迹进行了保留,你把学籍信息也一起带走了,一年后你又回来了,将自己的学籍给学校了,学校重新把你排课,安排考试,安排宿舍,你继续完成之前未完成的学业。
通过这两个故事,我想说的是我们再学校里面上学,就相当于进程再cpu上正在运行,我们去部队,就相当于进程从cpu下拿下来,同时需要将自己运行的重要数据信息带走,方便下次再上来的时候可以恢复你上次运行的数据以便可以继续跑程序。
那我们每次运行的后的数据都是放在哪里?
之前我们看过内存层状图,最上面的是寄存器,也是离cpu最近的,因为离的越近,速度越快,效率越高,cpu上面跑的程序将计算好的数据先放到寄存器上做临时保存,当时间片的时间到了,进程被拿下来了,我们就需要把寄存器上的数据带走保存起来,因为其他进程上面跑的时候此时寄存器的保存的数据是别的进程计算出来的数据,所以再我们的task_struct里面肯定还有一个保存寄存器里面值的结构,下次再运行的时候直接先将保存好的数据拂去出来放到寄存器上,运行再跑下面的代码,接下来画图带大家去理解:
所以cpu里面的寄存器是保存进程的临时数据—进程的上下文,进程的切换就是上下文保存和恢复的操作。
既然数据的问题解决了,那么我们的cpu是怎么知道我们程序下次从哪里开始运行呢,就好比上面,我们怎么知道下次上到cpu从第二行开始,原因就是我们的task_struct里面还有一个属性就是程序计数器pc:记录当前进程正在执行的指令的下一条指令的地址,下次再运行的时候,通过pc就可以找到上次运行结束后的下一行代码的位置了。
到这里大家应该知道进程切换是怎么去做的吧,希望大家可以理解。所以计算机里面的寄存器很多,但是每个都不大,因为存放的数据不大,而且每个进程继续后会被下一个上面的进程覆盖掉,也不需要保存。
所以我们一个函数的返回值是怎么做到被外部接收的这个问题也解决了,就是将函数的返回的数据先放到寄存器是,也叫临时数据,这也是我们平时说的,返回值不是直接将值返回给接受方,而是通过临时变量,这个临时变量就是寄存器,函数的返回值可以很好的帮助我们理解上面所说的内容。
二、总结
将到这里,我们进程优先级,如何根据优先级去调度,以及是怎么进行进程切换的,我们已经讲解完毕了,希望大家可以好好的吸收一下,这一块本身就一点难理解,因为cpu的速度太快了,我们感觉不到,所以才会感觉难,我们的感知都是一起运行的,而计算机里面大部分进程都是处于等待的,知识我们感觉不到,也希望大家不要思维定式,下来好好理解一下,那我们今天就说到这里,我们下篇再见