一、进程的并行与并发以及进程切换
并行性指的是多个任务同时在不同的CPU或者计算单元上执行,以实现更高的执行效率和性能。在并行执行中,每个任务都在独立的处理单元上独立执行,并且可以同时进行。
并发行指的是多个任务在同一时间段内交替执行,并且共享相同的计算资源。在并发执行中,多个任务按照一定的调度策略交替执行,每个任务都会分配一定的执行时间片,然后切换到下一个任务。并发性通常用于处理I/O密集型的任务,如网络通信、用户界面,以实现任务之间的响应性和交互性。
注意:并发性并不要求任务真正地同时执行,而是通过快速切换和时间片分配来模拟任务的同时执行。这种交替执行的方式可以在单个处理器上实现,并通过调度器的调度算法来决定任务的执行顺序和时间片分配。

二、进程并发与进程优先级是否有矛盾
进程切换的本质是推动多个进程同时推进,而进程优先级是确定了进程的排队顺序,那么就存在到底是按优先级优先执行优先级高的进程还是按找进程切换,排队的同时进行多个进程,二者是否会存在矛盾?
进程切换是操作系统在多任务环境下,将CPU的控制权从一个进程切换到另一个进程的过程。进程切换可以由多种原因触发,例如时间片轮转、中断处理、阻塞等。在进程切换时,操作系统会保存当前进程的上下文,并加载下一个进程的上下文,以便新的进程可以继续执行。
进程优先级是操作系统为了管理和调度进程而分配给每个进程的相对优先级。不同的进程可以有不同的优先级,优先级较高的进程通常具有更高的调度优先权,有更多的机会获得CPU的执行时间。
进程切换和进程优先级之间的关系可以通过调度算法来体现。调度算法决定了在进行进程切换时,系统选择哪个进程来执行。一些调度算法会考虑进程的优先级,倾向于选择优先级较高的进程进行调度,一些情况下,这种调度算法可以给予高优先级的进程更多的占用时间。
然而,并非所有的调度算法都严格遵循进程优先级。一些调度算法可能更加关注公平性和均衡性,以确保所有进程都能获得公平的执行机会,而不会过度偏袒高优先级进程。这种情况下,进程切换的决策可能更多地依赖于其他因素,如时间片轮转或就绪队列中的进程顺序。
三、CPU内部的寄存器及其作用
CPU中的寄存器是一组用于临时存储数据和指令的内部存储器。寄存器是CPU内部最快速、最直接可访问的存储单元,其速度比其他存储器(如缓存、内存)更快。
那么,这些寄存器,又对进程切换产生了什么样的影响呢?我们首先来思考这样一个问题,我们的临时变量是如何被外部接收到的呢?比如我们c中写一个函数,函数返回一个值被main中的一个变量接收,这是如何实现的呢?
3.1临时变量的保存
其实,这是寄存器(eax)在CPU内部起了作用,我们函数中的return语句,在执行时被当做了一条汇编语言的move指令,就会将当前所产生的需要返回的临时变量的值移动到eax中保存起来,之后再进行栈帧的销毁,当需要这个函数返回的临时变量的值的时候,直接去eax寄存器中寻找,这样一来,虽然临时变量已经被销毁,但是它的值被保存到了寄存器中。
3.2进程执行状态的纪录
每一个进程不是一次就能执行完程序并退出的,CPU为了保证一定程度的多个进程同时推进,会根据时间片来进行进程切换,但是当前程序的运行进度需要被保存下来,以便下次轮到该进程直接从上一次的进度开始执行,这就叫做进程的切出与切入。
这得益于CPU中的另一个寄存器:程序计数器(PC),程序计数器是一个特殊的寄存器,用于存储当前正在执行的指令的地址或下一条指令的地址。它在指令执行期间不断更新,帮助CPU跟踪程序的执行顺序。
还有,本次进程在被从CPU上剥离之前(进程未退出),要将自己进程的执行情况(包括数据等保存在CPU寄存器上的资源中的内容也一并带走,并将其打包放在自己的PCB中的一个结构体中,然后再进行进程切换。

四、Linux内核进程调度队列

我们只是选择其中的绿色部分的数据成员来进行讲解,目的是了解进程的运行对队列和进程切换的原理以及简单的了解调度算法的原理。
4.1优先级
在Linux中一般进程一般分为实时进程和普通进程,我们日常所写的代码所生成的可执行程序什么的,一般都是普通进程,实时进程优先级为0~99,在运行对列中的保存位置也是在下标0~99处,即优先级与下标一一映射,而普通优先级却不同,普通进程的优先级是60~99,其保存位置是在运行队列的下标100~139处,属于映射保存,也就是优先级为60的进程保存在下标100的位置,优先级为61的保存在下标101的位置,以此类推,实时进程之所以保存在运行队列前端,是因为其要优先被响应和执行,这个我们下面还会说到。
这里可能会产生一个疑问,为什么实时进程的优先级是0~99的同时普通进程还能是60~99,这两者不会冲突吗?
事实上,Linux内核上是可以直接将这两种进程结构区分开的,之所以这两者的进程优先级数字有重叠,是因为历史原因造成的,这里我们只需要知道,实时进程的优先级和普通进程的优先级不是单纯靠优先级的数字进行区分的,他们之间更多的是结构上的不同,举个例子,同一个餐厅,老师排队对列中的第5名和学生排队队列的第5名意义肯定是不一样的。

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

4.3实时进程与普通进程的不同处理方式(一般调度算法)
在上面我们已经简单阐述了普通进程在执行过程中遇到其他优先级的普通进程都要将其直接加入到过期队列进行等待,其实这里还有好几种情况,我们来分类进行讲解:
需要注意的是,进程的调度和执行顺序还受到一些其他因素的影响,例如进程的优先级设置、调度策略、CPU负载等等。我们这里只是一般的处理方式,具体的行为可能会因操作系统的版本、配置和调度算法的不同而有所差异。
1.当前正在执行实时进程:
<1>新加入了一个实时进程
实时进程通常具有固定的时间限制,需要在规定的时间内完成任务,因此,将其放在了运行队列的头部部分,目的是让其在遍历执行的时候能够更早的遍历,像汽车的刹车系统、控制系统和信号系统等等都属于实时进程任务,所以,当正在执行实时进程时,如果新加入的实时进程比当前正在执行的实时进程的优先级高,那么新加入的实时进程就会直接加入到活动对列的指定下标处,并抢占CPU时间,使得当前进程从CPU上剥离,但是被剥离的实时进程在退出时还是会保存退出信息,并在活动队列原来的位置继续等待时间片轮转。
<2> 新加入了一个普通进程
这就没什么悬念了,当实时进程正在执行时,新加入的普通进程会被放入活动队列等待执行。在实时调度策略下,实时进程具有较高的优先级,优先级比普通进程更高。因此,当实时进程处于执行状态时,普通进程以较低的优先级进入活动队列,并等待其轮到执行。
2.当正在执行普通进程时:
<1> 新加入了一个实时进程
实时进程通常具有固定的时间限制,需要在规定的时间内完成任务,而普通进程则采用时间片轮转的方式进行调度。所以当前的普通进程必须要为这个新的实时进程“让路”,在 Linux 中,实时进程具有更高的优先级,当实时进程准备好执行时,它会打断正在执行的普通进程。被打断的普通进程会退出执行,并被放入过期队列。过期队列中的进程等待重新调度,并有可能在下一次的调度中获得执行机会。
<2>新加入一个普通进程
这就是我们之前说的那种情况,这种情况下,不论新加入的普通进程优先级高于还是低于当前正在执行的进程,一律都将其加入到过期队列中等待下一轮调度执行。
五、bitmap与进程调度算法
我们在遍历队列寻找哪一处下标存在待执行的进程的时候,除了按照下标顺序从小到大执行之外,我们还需要考虑有实时进程插入的情况,那么,如何判断当前正在执行的进程的时候有实时进程插入了呢?一个暴力的做法是在每次执行进程之前都先遍历一遍该进程对应的映射在运行队列上的下标之前的所有下标,看看是否有进程存在,但是这样时间复杂度无疑是非常慢的,于是,我们便可以通过用比特位的0或者1的变化来简单的判断该比特位对应的位置上有无进程存在,bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个比特位表示队列是否为空,这样,便可以大大提高查找效率!
本文探讨了进程的并行与并发概念,强调了进程切换的原理,以及进程优先级、CPU寄存器在其中的作用,特别是Linux内核的进程调度队列设计,包括实时进程与普通进程的处理,以及使用bitmap优化调度效率的方法。
421

被折叠的 条评论
为什么被折叠?



