Linux之进程切换与命令行参数

我们在了解了Linux进程状态和优先级的概念,初步掌握了进程状态的相关知识,现在就从这些概念开始说起,进一步了解Linux进程切换的原理以及运行队列的进程排队机制,以及简单了解一下有效避避免产生进程饥饿的进程调度和插队算法,顺便讲一下命令行参数的相关知识,为后面学习环境变量做铺垫

进程的并行,并发&进程切换

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级.
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰.
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行.
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发.

并行性指的是多个任务同时在不同的CPU或计算单元上执行,以实现更高的执行效率和性能。在并行执行中,每个任务都在独立的处理单元上独立执行,并且可以同时进行。

并发性,与并行性不同,并发性指的是多个任务在同一时间段内交替执行,并共享相同的计算资源。在并发执行中,多个任务按照一定的调度策略交替执行,每个任务都会分配一定的执行时间片,然后切换到下一个任务。并发性通常用于处理I/O密集型的任务,如网络通信、用户界面等,以实现任务之间的响应性和交互性。

 需要注意的是,并发性并不要求任务真正地同时执行,而是通过快速切换时间片分配来模拟任务的同时执行。这种交替执行的方式可以在单个处理器上实现,并通过调度器的调度算法来决定任务的执行顺序和时间片分配。Linux支持进程基于时间片的轮转式抢占CPU内核资源.

并发+并行:


CPU内部的寄存器

CPU中的寄存器是一组用于临时存储数据和指令的内部存储器。寄存器是CPU内部最快速、最直接可访问的存储单元,其速度比其他存储器(如缓存、内存)更快。

那么,这些寄存器,又对进程切换产生了什么样的影响呢?我们首先来思考这样一个问题,我们的临时变量是如何被外部接收到的呢?比如我们c中写一个函数,函数返回一个值被main中的一个变量接收,这是如何实现的呢?

临时变量的保存

其实,这是寄存器(eax)在CPU内部起了作用,我们函数中的return语句,在执行时被当做了一条汇编语言的move指令,就会将当前所产生的需要返回的临时变量的值移动到eax中保存起来,之后再进行栈帧的销毁,当需要这个函数返回的临时变量的值的时候,直接去eax寄存器中寻找,这样一来,虽然临时变量已经被销毁,但是它的值被保存到了寄存器中。

进程执行状态的记录

每一个进程不是一次就能执行完程序并退出的,CPU为了保证一定程度的多个进程同时推进,会根据时间片来进行进程切换,但是当前程序的运行进度需要被保存下来,以便下次轮到该进程直接从上一次的进度开始执行,这就叫做进程的切出与切入。
这得益于CPU中的另一个寄存器:程序计数器(eip或者PC),程序计数器是一个特殊的寄存器,用于存储当前正在执行的指令的地址或下一条指令的地址。它在指令执行期间不断更新,帮助CPU跟踪程序的执行顺序。

还有,本次进程在被从CPU上剥离之前(进程未退出),要将自己进程的执行情况(包括数据等保存在CPU寄存器上的资源中的内容也一并带走,并将其打包放在自己的PCB中的一个结构体中,然后再进行进程切换 ,举个例子:

大学的时候可能会有征兵,  假如你和室友一起去征兵, 但你因为一些因素没有征兵成功, 室友去当兵两年回来后发现自己挂了20多门课, 因为他走之前没有和学校说明保留学籍, 正确做法应该是在当兵前先和学校打招呼,把档案袋带走, (把学籍上下文进行保护)退伍后也应该和学校说明一声, 将档案袋归还, 恢复一下学籍, 从而进行之后的教学活动(恢复学籍上下文). 对于他离开原舍友加入新舍友的行为, 叫作完成了一次进程的切出和切入

上面的例子说明: 任何的保留都是为了恢复, 不断的对自己的上下文进行保护和恢复的过程, 叫作进程的上下文切换.

一个进程如果运行完了, 那么直接切换为死亡状态即可, 关键是如果运行不完但时间片到了进程需要切换了呢?

那么计算机在保存进程的硬件上下文时,保存的是寄存器还是寄存器的内容?

肯定是寄存器的内容(数据)

那寄存器的内容保存在哪里?

早期是保存在PCB结构体里, 用到的时候再读取回寄存器, 但现在的上下文数据并不是直接在PCB里保存, 但与PCB有关系, 有一个概念叫tss(任务状态段), 但太复杂先不考虑, 先认为保存在PCB里.

结论: 将cpu内的寄存器数据保存到进程PCB中(简单理解), 本质是CPU寄存器的内容, 保存到内存中

那保存完数据之后, 寄存器的内容会被清空吗?

不会, 事实上,一个进程被取下来时CPU并不会删除它的临时数据而是当下一个进程被放入时,用下一个进程的数据将上一个进程的数据覆盖了!


Linux2.6内核进程调度队列

我们只是选择其中的蓝色和红色部分的数据成员来进行讲解,目的是了解进程的运行对队列和进程切换的原理以及简单的了解调度算法的原理。 

有一种操作系统叫实时操作系统和分时操作系统, 平时使用的操作系统大部分叫分时操作系统,用时间片来平衡的调度进程 , 实时操作系统与一般的操作系统相比,最大的特色就是“实时性”,如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。这种特性保证了各个任务的及时执行。

比如车载操作系统选择实时的操作系统就更合适, 刹车系统如果用电脑去控制, 防止一脚刹车踩死轮胎打滑翻车, 需要电脑去控制相应的力度, 但如果此时是分时操作系统, 公平调度进程, 控制刹车力度的进程被延误0.几秒, 都会出现安全问题, 所以分时操作系统不合适.

linux系统 实时操作系统和分时操作系统都支持


优先级

 在Linux中一般进程一般分为实时进程和普通进程,我们日常所写的代码所生成的可执行程序什么的,一般都是普通进程,实时进程优先级为0~99,在运行对列中的保存位置也是在图中queue数组中的下标0~99处,即优先级与下标直接映射,而普通进程(分时进程)优先级却不同普通进程的优先级是60~99,其保存位置是在运行队列的下标100~139处,属于相对映射保存,也就是优先级为60的进程保存在下标100的位置,优先级为61的保存在下标101的位置,以此类推,实时进程之所以保存在运行队列前端,是因为其要优先被响应和执行,这个我们下面还会说到。

这里可能会产生一个疑问,为什么实时进程的优先级是0~99的同时普通进程还能是60~99,这两者不会冲突吗?

事实上,Linux内核上是可以直接将这两种进程结构区分开的,之所以这两者的进程优先级数字有重叠,是因为历史原因造成的,这里我们只需要知道,实时进程的优先级和普通进程的优先级不是单纯靠优先级的数字进行区分的,他们之间更多的是结构上的不同,举个例子,同一个餐厅,老师排队对列中的第5名和学生排队队列的第5名意义肯定是不一样的。

活动队列与过期队列

活动队列和过期队列在结构上是两个一模一样的运行队列,只是名字不同罢了,为了搞清楚这两个队列的区别,我们先来看为什么要引入过期队列,注意:我们下面的讨论均站在进程都是分时进程的前提条件下

在活动队列执行进程的过程中,假设我们正在执行普通优先级为80的进程,此时又新来了一个普通进程,优先级为70,那么,此时如果你是OS,你会怎么处理这个新加入的进程呢?很明显,这个新的普通进程比当前正在执行的进程优先级高,按理来说应该在当前执行的进程的前面执行,但是,如果我们将当前执行的进程从cpu剥下,转而执行新的进程的话,长此以往,是不是优先级低的进程就可能一直在等待,就会导致饥饿问题。由于普通进程没那么急,操作系统又想实现进程间的公平和协调性问题,尽可能让每一个进程都有执行的机会,为此,我们引入第二个运行队列结构,和运行队列结构完全一致,称之为过期队列,此时,当再次有新的普通进程加入时,我们不将其插入到活动队列,而是将其直接插入到过期队列中,这样,活动队列中的进程就会慢慢减少,直到最后减少为空(一个进程任务都没有了),那么此时,我们就可以将过期队列和活动队列的数据互换(改变一下指针的执行就行了),这样,就能一定程度的减少饥饿问题,保证公平和均衡性。


实时进程与普通进程的不同处理方式(一般 调度算法)

在上面我们已经简单阐述了非实时进程在执行过程中遇到其他优先级的非实时进程都要将其直接加入到过期队列进行等待,其实这里还有好几种情况,我们来分类进行讲解:

需要注意的是,进程的调度和执行顺序还受到一些其他因素的影响,例如进程的优先级设置、调度策略、CPU负载等等。我们这里只是一般的处理方式,具体的行为可能会因操作系统的版本、配置和调度算法的不同而有所差异。

1.当前正在执行实时进程:

         <1>新加入了一个实时进程

          实时进程通常具有固定的时间限制,需要在规定的时间内完成任务,因此,将其放在了运行队列的头部部分,目的是让其在遍历执行的时候能够更早的遍历,像汽车的刹车系统、控制系统和信号系统等等都属于实时进程任务,所以,当正在执行实时进程时,如果新加入的实时进程比当前正在执行的实时进程的优先级高,那么新加入的实时进程就会直接加入到活动对列的指定下标处,并抢占CPU时间,使得当前进程从CPU上剥离,但是被剥离的实时进程在退出时还是会保存退出信息,并在活动队列原来的位置继续等待运行。

         <2> 新加入了一个非实时进程

         这就没什么悬念了,当实时进程正在执行时,新加入的普通进程会被放入活动队列等待执行。在实时调度策略下,实时进程具有较高的优先级,优先级比普通进程更高。因此,当实时进程处于执行状态时,普通进程以较低的优先级进入活动队列,并等待其轮到执行。

2.当正在执行普通进程时:

       <1> 新加入了一个实时进程

       实时进程通常具有固定的时间限制,需要在规定的时间内完成任务,而普通进程则采用时间片轮转的方式进行调度。所以当前的普通进程必须要为这个新的实时进程“让路”,在 Linux 中,实时进程具有更高的优先级,当实时进程准备好执行时,它会打断正在执行的普通进程。被打断的普通进程会退出执行,并被放入过期队列。过期队列中的进程等待重新调度,并有可能在下一次的调度中获得执行机会。

       <2>新加入一个普通进程

        这就是我们之前说的那种情况,这种情况下,不论新加入的普通进程优先级高于还是低于当前正在执行的进程,一律都将其加入到过期队列中等待下一轮调度执行。


 bitmap与进程调度算法

我们在遍历队列寻找哪一处下标存在待执行的进程的时候,除了按照下标顺序从小到大执行之外,我们还需要考虑有实时进程插入的情况,那么,如何判断当前正在执行的进程的时候有实时进程插入了呢?一个暴力的做法是在每次执行进程之前都先遍历一遍该进程对应的映射在运行队列上的下标之前的所有下标,看看是否有进程存在,但是这样时间复杂度无疑是非常慢的,于是,我们便可以通过用比特位的0或者1的变化来简单的判断该比特位对应的位置上有无进程存在,bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个比特位表示队列是否为空,这样,便可以大大提高查找效率!


进程的并发与进程优先级是否矛盾 

进程切换的本质是推动多个进程同时推进,而进程优先级是确定了进程的排队顺序,那么就存在到底是按优先级优先执行优先级高的进程还是按照进程切换,排队的同时进行多个进程,二者是否会存在矛盾?

进程切换是操作系统在多任务环境下,将CPU的控制权从一个进程切换到另一个进程的过程。进程切换可以由多种原因触发,例如时间片轮转、中断处理、阻塞等。在进程切换时,操作系统会保存当前进程的上下文,并加载下一个进程的上下文,以便新的进程可以继续执行。

进程优先级是操作系统为了管理和调度进程而分配给每个进程的相对优先级。不同的进程可以有不同的优先级,优先级较高的进程通常具有更高的调度优先权,有更多的机会获得CPU的执行时间。

进程切换和进程优先级之间的关系可以通过调度算法来体现。调度算法决定了在进行进程切换时,系统选择哪个进程来执行。一些调度算法会考虑进程的优先级,倾向于选择优先级较高的进程进行调度,一些情况下,这种调度算法可以给予高优先级的进程更多的占用时间。

然而,并非所有的调度算法都严格遵循进程优先级。一些调度算法可能更加关注公平性和均衡性,以确保所有进程都能获得公平的执行机会,而不会过度偏袒高优先级进程。这种情况下,进程切换的决策可能更多地依赖于其他因素,如时间片轮转或就绪队列中的进程顺序.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值