目录
一、任务划分(II)
二、任务控制块(TCB)(II)
三、任务的状态及状态转换(II)
四、任务队列(II)
五、任务管理机制(II)
六、任务调度(II)
6.1 调度时机
6.2 调度方式
6.3 调度算法性能指标和分类
6.4 任务调度算法(II)
1、先来先服务算法
2、短作业优先算法
3、时间片轮转算法
4、优先级算法
5、使用率平衡算法
6、单调速率调度算法
7、最早截止期限优先动态调度(EDF)算法
七、任务同步与互斥(III)
1、任务之间有以下几种关系:
2、任务互斥
1、竞争条件
2、任务互斥
3、临界区
4、信号量
5、死锁
八、 优先级反转(II)
1、优先级反转
2、优先级继承
3、优先级天花板
九、任务通信(II)
1、共享内存
2、消息传递
3、管道
十、高可靠性操作系统分区调度和分区通信 (II)
原则1:是将同一个外设的访问放在一个任务中。
对每个独立的硬件(例如串行通信端口)进行操作的驱动程序段放在一个任务中。也就是说,要想对某个设备资源进行操作,只有依靠执行相应的任务来实现。这样无论何时切换任务,都不会对任何独立的“外设”造成影响。这样做能够避免嵌入式操作系统的特殊问题——资源冲突和重入问题,而且利于系统维护与升级。各个任务之间要实现通信,可以调用os_send_signal函数及全局变量来实现。
所谓“资源冲突”,就是任务A在访问某个资源时,恰好发生了任务切换——由任务A切换到任务B,任务B也访问这个资源且改变了它的状态,这样当再次执行任务A时,就可能发生冲突或带来不确定性。而所谓“重入”,是指假设任务A在运行某个函数,发生任务切换后,任务B也运行这个函数,这样就会破坏任务A执行这个函数时的现场,从而可能导致任务A执行函数时结果不正确。这种问题尤其容易出现在串行接口器件的操作中,例如串口,串行的A/D、D/A器件等。
原则2:是要通过任务分割提高系统的实时性。
在嵌入式多任务实时系统中,任务是指一个程序分段。这个程序分段被操作系统当作一个基本单元来调度。典型地,每个任务都是一个无限的循环。RTOS本质上就是嵌入的实时内核,它负责管理各个任务,或者说是为每个任务分配CPU时间,并且负责任务之间的通信。实时内核可分为可剥夺型和不可剥夺型两类。因此,按照所使用内核的不同,嵌入式实时系统也可分为两类:使用不可剥夺型内核的嵌入式实时系统和使用可剥夺型内核的嵌入式实时系统。
定义:在操作系统当中,用来描述和管理一个任务的数据结构。
系统为每 一个任务都维护了一个相应的TCB, 用来保存该任务的各种相关信息,TCB 的内容主要包括任务的管理信息、CPU 上下文信息和资源管理信息。
(1)任务的管理信息:包括任务的标识ID 、任务的状态、任务的优先级、任务的调度信息、任务的时间统计信息、各种队列指针等。
(2)CPU上下文信息:指 CPU 中各寄存器的当前值,包括通用寄存器、PC 寄存器、 程序状态字、栈指针等。前面所述进程中的逻辑寄存器就是TCB 当中的相应字段,是一 些内存变量。另外,在实际的嵌入式系统中,CPU 上下文信息不一定直接存放在TCB 当中,而是存放在任务的栈中,可以通过相应的栈指针来访问。
(3)资源管理信息:如果在操作系统中,任务表示的是进程,则还应包含一些资源管理方面的信息,如段表地址、页表地址等存储管理方面的信息;根目录、文件描述字等文件管理方面的信息。
在嵌入式操作系统中,可以用TCB 来描述任务的基本情况以及它的运行变化过程,把
TCB 看成是任务存在的唯一标志。具体来说,当需要创建一个新任务的时候,就为它 生成一个 TCB, 并初始化这个 TCB 的内容。当需要中止一个任务的时候,只要回收它的TCB 即可。而对于任务的组织和管理,也可以通过对它们的TCB 的组织和管理来实现。
在多道程序系统中,任务是独立运行的实体,需要参与系统资源的竞争,只有在所需资源都得到满足的情形下,才能在 CPU上运行。因此,任务所拥有的资源情况是在不断变化的,这导致任务的状态也表现出不断变化的特性。不同的嵌入式操作系统对任务状态的定义不尽相同,但是一般来说,它们都会具备以下的三种基本状态。
●运行状态 (Running): 任务占有CPU, 并在CPU 上运行。显然,处于此状态的任 务个数必须小于或等于 CPU 的数目。如果在系统当中只有一个 CPU 的话,那么 在任何一个时刻,最多只能有一个任务处于运行状态。
●就绪状态 (Ready): 任务已经具备了运行的条件,但是由于CPU 正忙,正在运行 其他的任务,所以暂时不能运行。不过,只要把CPU 分给它,它就能够立刻执行。
●阻塞状态(Blocked): 也叫等待状态 (Waiting)。任务因为正在等待某种事件的发生而暂时不能够运行。例如,它正在等待某个 I/O 操作的完成,或者它跟某个任务之间存在着同步关系,正在等待该任务给它发信号。此时,即使CPU已经空闲下来了,它也还是不能行。
在一定条件下,任务会在不同的状态之间来回转换,如图4-18所示。对于任务的三
种状态,可以有四种转换关系。
●运行→ 阻塞:任务由于等待某个事件而被阻塞起来。例如, 一个任务正在CPU 上运行,这时它需要用户输入一个字符。由于CPU 的运行速度远远高于I/O 设备的 处理速度,因此操作系统不会允许该任务继续占用CPU, 在那里空等,而是把它变成阻塞状态,然后调用其他的任务去运行。
●运行 →就绪: 一个任务正在CPU 上运行,这时由于种种原因(如该任务的时间片用完,或另一个高优先级任务就绪),调度器选择了另一个任务去运行。这样对于当前的任务来说,就从运行状态变成了就绪状态。
●就绪 →运行:处于就绪状态的任务被调度器选中去运行。
●阻塞 →就绪: 一个任务曾经因为等待某个事件而被阻塞起来,如果它等待的事件 发生了,那么该任务就从阻塞状态变成了就绪状态,从而具备了继续运行的条件。
如前所述,在一个多任务操作系统中,各个任务的状态是经常变化的,有时处于运行状态,有时处于就绪状态,有时又处于阻塞状态。即便同是阻塞状态,引发阻塞的原因可能又各不相同,有的是因为等待 I/O 操作,有的是因为任务之间的同步。因此,在 一个操作系统当中,采用什么样的方式来组织它的所有任务,将直接影响到对这些任务的管理效率。
通常的做法是采用任务队列的方式,由操作系统来维护一组队列,用来表示系统当中所有任务的当前状态,不同的状态用不同的队列来表示。例如,处于运行状态的所有任务构成了运行队列,处于就绪状态的所有任务构成了就绪队列,而对于处于阻塞状态的任务,则要根据它们阻塞的原因,分别构成相应的阻塞队列。然后,对于 系统当中的每一个任务,根据它的状态把它的TCB 加入到相应的队列当中去。如果一个 任务的状态发生变化,例如,从运行状态变成就绪状态,或者从阻塞状态变成就绪状态,这时,就要把它的TCB 从一个状态队列中脱离出来,加入到另一个队列当中去。
在核的设计过程中,最先应考虑的是任务的状态以及迁移时序,然后根据此状态设计相应的队列,如就绪队列、等待队列等。核时钟也依赖任务的状态。可以看出,任务管理实现的核心和基础是任务状态和迁移时序。
在多道程序操作系统中,经常会出现多个任务同时去竞争 CPU 的情形,换句话说, 就是在系统的就绪队列中,有两个或多个任务同时处于就绪状态。假设在系统中只有一个
CPU, 而且这个 CPU 已经空闲下来了,现在的问题就是:对于就绪队列当中的那些任 务,应该选择哪一个去运行?在操作系统当中,负责去做出这个选择的那一部分程序, 就称为是调度器,而调度器在决策过程中所采用的算法,就称为是调度算法。如果从资源管理的角度来看,也可以把调度器看成是CPU 这个资源的管理者。
任务调度的首要问题是何时进行调度,即调度发生的时机。 一般来说,在以下五种情形下,可能会发生任务的调度。
(1)当一个新的任务被创建时,需要做出一个调度决策,是立即执行这个新任务还是继续执行父任务?
(2)当一个任务运行结束时,它不再占用 CPU, 这时调度器必须作出一个决策,从就绪队列中选择某个任务去运行。如果此时没有任务处于就绪状态,系统一般会调度一个特殊的空闲任务。
(3)当一个任务由于 I/O 操作、信号量或其他原因被阻塞时,也必须另选一个任务运行。
(4)当 一个 I/O 中断发生时,表明某个 I/O 操作已经完成,而等待该I/O 操作的任务 将从阻塞状态变为就绪状态,此时可能需要做出一个调度决策,是立即执行这个新就绪的任务,还是继续执行刚才被中断的那个任务。
(5)当一个时钟中断发生时,表明一个时钟节拍已经结束。这时,可能会唤醒一些 延时的任务,使它们变为就绪状态,也可能会发现当前任务的时间片已用完,从而把它变为就绪状态。在这些情形下,也需要调度器来重新调度。
任务调度的第二个问题是调度的方式,主要有两种方式:不可抢占调度和可抢占调度。
(1)不可抢占方式 (non preemptive)。如果一个任务被调度程序选中,就会一直地运行下去,直到它因为某种原因(如 I/O 操作或任务间的同步)被阻塞了,或者它主动地交出了 CPU 的使用权。在不可抢占的调度方式下,当出现调度时机当中的前三种情形时, 即新任务创建、任务运行结束及任务被阻塞,都有可能会发生调度。而对于第四种和第 五种情形,即发生各种中断的时候,虽然也会有中断处理程序,但是它并不会去调用调度程序。因此,当中断处理完成后,又会回到刚才被打断的任务继续执行。
(2)可抢占方式 (preemptive) 。当一个任务正在运行的时候,调度程序可以去打断它,并安排另外的任务去运行。在这种调度方式下,对于调度时机当中的所有五种情形, 都有可能会发生调度。另外,在其他的一些情形下,假设调度算法是按照任务的优先级 来进行调度,那么一旦在就绪队列当中有任务的优先级高于当前正在运行的任务,就可能立即进行调度,转让CPU。实时操作系统大都采用了可抢占的调度方式,使一些比较重要的关键任务能够打断那些不太重要的非关键任务的执行,以确保关键任务的截止时间能够得到满足。
实时操作系统大都采用了可抢占的调度方式,使一些比较重要的关键任务能够打断那些不太重要的非关键任务的执行,以确保关键任务的截止时间能够得到满足。
调度算法的性能指标包括:
●响应时间:调度器为一个就绪任务进行上下文切换时所需的时间,以及任务在就 绪队列中的等待时间;
●周转时间: 一个任务从提交到完成所经历的时间;
●调度开销:调度器在做出调度决策时所需要的时间和空间开销;
●公平性:大致相当的两个任务所得到的CPU 时间也应该是大致相同的。另外,要 防止饥饿,即某个任务始终得不到处理器去运行;
●均衡性:要尽可能使整个系统的各个部分 (CPU、I/O) 都忙起来,提高系统资源的使用效率;
●吞吐量:单位时间内完成的任务数量。
在这些指标当中,有一些是可以共存的,也有一些是相互牵制的。因此,对于一个 实际的调度算法来说,这些指标不可能全部都实现,而是要根据系统的需要,有一个综合的权衡和折衷的过程。
先来先服务算法(First Come First Served,FCFS),也叫做先进先出算法(First In First
Out,FFO),是最简单的一种调度算法。按照任务到达的先后次序来进行调度(如图4-19所示)。它是一种不可抢占的调度方式,如果当前任务占用着CPU在运行,那么就要一直等到它执行完毕或者因为某种原因被阻塞,才会让出CPU给其他的任务。另外,对于一个被阻塞的任务,当它被唤醒之后,就把它放在就绪队列的末尾,重新开始排队。先来先服务算法的最大优点就是简单,易于理解也易于实现。它的缺点也很明显:
一批任务的平均周转时间取决于各个任务到达的顺序,如果短任务位于长任务之后,那
么将增大平均周转时间。
为了改进FCFS算法,减少平均周转时间,人们又提出了短作业优先算法(Shortest Job
Frs,SF)。SJF算法的基本思路是:各个任务在开始执行前,必须事先预计好它的执行时间,然后调度算法将根据这些预计时间,从中选择用时较短的任务优先执行。SJF算法有两种实现方案:
不可抢占方式:当前任务正在运行的时候,即使来了一个比它更短的任务,也不会被打断,只有当它运行完毕或者是被阻塞时,才会让出CPU,进行新的调度。
可抢占方式:如果一个新的短任务到来了,而且它的运行时间要小于当前正在运行的任务的剩余时间,那么这个新任务就会抢占CPU去运行。这种方法,也称为最短剩余时间优先算法(Shortest Remaining Time First,.SRTF)。
不可抢占的SJF算法如图420所示,由于任务T3的执行时间最短,所以首先被调度
运行,其次是T1,和T2.
可以证明,对于一批同时到达的任务,采用SJF算法将得到一个最小的平均周转时间。例如,假设有四个任务A、B、C、D,它们的运行时间分别是a、b、c和d,假设它们的到达时间是差不多的,调度顺序为A、B、C、D。那么任务A的周转时间为a,b的周转时间为a+b,C的周转时间为a+b+c,D的周转时间为a+b+c+d,因此,最后的平均周转时间为(4a+3b+2c+d)/4,从这个式子来看,显然,只有当a
时间片轮转算法(Round Robir,RR)的基本思路是:把系统当中的所有就绪任务按照先来先服务的原则,排成一个队列。然后,在每次调度的时候,把处理器分派给队列当中的第一个任务,让它去执行一小段CPU时间,或者叫时间片。当这个时间片结束的时候,如果任务还没有执行完的话,将会发生时钟中断,在时钟中断里面,调度器将会暂停当前任务的执行,并把它送到就绪队列的末尾,然后执行当前的队首任务。反之,如果一个任务在它的时间片用完之前就已经运行结束了或者是被阻塞了,那么它就会立即让出CPU给其他的任务。
时间片轮转法的优点是:
公平性:各个就绪任务平均地分配CPU的使用时间。例如,假设有n个就绪任务,
那么每个任务将得到1/n的CPU时间。
活动性:每个就绪任务都能一直保持着活动性,假设时间片的大小为q,那么每
个任务最多等待(n-1)q这么长的时间,就能再次得到CPU去运行。
在采用时间片轮转算法时,时间片的大小q要适当选择,如果选择不当,将影响到
系统的性能和效率。
如果q太大,每个任务都在一个时间片内完成,这就失去了轮转法的意义,退化为先来先服务算法了,这就使各个任务的响应时间变长。
如果q太小,每个任务就需要更多的时间片才能运行完,这就使任务之间的切换次数增加,从而增大了系统的管理开销,降低了CPU的使用效率。
因此,如何来选择一个合适的q值,既不能太大,也不能太小,这是时间片轮转法
的最大问题。一般来说,这个值选在20~50ms是比较合适的。
优先级调度算法的基本思路是:给每一个任务都设置一个优先级,然后在任务调度的时候,在所有处于就绪状态的任务中选择优先级最高的那个任务去运行。例如,短作业优先算法其实也是一个优先级算法,每个任务的优先级就是它的运行时间,运行时间越短,优先级越高。
优先级算法可以分为两种:可抢占方式和不可抢占方式。它们的区别在于:当一个任务正在运行的时候,如果这时来了一个新的任务,其优先级更高,那么在这种情形下,是立即抢占CPU去运行新任务,还是等当前任务运行完了再说。在任务优先级的确定方式上,可以分为静态方式和动态方式两种。
静态优先级方式:在创建任务的时候就确定任务的优先级,并且一直保持不变直到任务结束。优先级的确定可以依据任务的类型或重要性,例如,系统任务的优先级要高于用户任务,实时任务的优先级要高于非实时任务。静态优先级方式有一个很大的缺点:高优先级的任务会一直占用着CPU运行,而那些低优先级的任务可能会长时间地得不到CPU,一直处于“饥饿”状态。
动态优先级方式:在创建任务的时候确定任务的优先级,但是该优先级可以在任务的运行过程中动态改变,以便获得更好的调度性能。例如,为了防止静态优先级方式中出现的“饥饿”现象,系统可以根据任务占用CPU的运行时间和它在就绪队列中的等待时间来不断地调整它的优先级。这样,即便是一个优先级比较低的任务,如果它在就绪队列中的等待时间足够长,那么它的优先级就会不断提高,最终可以被调度执行。
在优先级算法中,高优先级的任务将抢占低优先级的任务。对于优先级相同的任务,通常的做法是把任务按照不同的优先级进行分组,然后在不同组的任务之间使用优先级算法,而在同一组的各个任务之间使用时间片轮转法。
采用优先级调度算法,还有一个问题就是可能会发生优先级反转的现象。在理想情况下,当高优先级任务处于就绪状态后,会立即抢占低优先级任务而得到执行。但在实际系统当中,在各个任务之间往往需要用到各种共享资源,如I/O设备、信号量、邮箱等等。在这种情形下,可能会出现高优先级任务被低优先级任务阻塞,等待它释放资源,而低优先级任务又在等待中等优先级任务的现象,这种现象称为“优先级反转”。
使用率平衡算法是一种优化资源利用率的算法,它的核心思想是通过动态调整资源分配,使得各个资源单元的负载均衡,从而提高整个系统的性能和效率。
首先,使用率平衡算法需要监控每个资源单元的实时负载情况,包括CPU使用率、内存占用率、磁盘I/O等。这些信息可以通过系统提供的监控工具或自定义的监控机制获取。
然后,算法会根据这些负载情况,分析资源的利用情况,找出可能存在的瓶颈或过载的资源单元。这可以通过一些性能指标或数学模型来实现,如响应时间、吞吐量、资源利用率等。
接下来,算法会根据负载情况和其他约束条件,动态调整资源分配。这可以通过一些调度策略或启发式算法来实现,如轮询调度、最少连接调度、预测调度等。这些策略可以根据实际情况进行选择和调整,以达到最佳的效果。
最后,使用率平衡算法会持续监控资源负载情况,并不断调整资源分配,以保持负载的均衡和系统的稳定运行。同时,算法还会根据历史数据和趋势分析,预测未来的负载情况,并提前做出相应的调整。
总的来说,使用率平衡算法是一种动态、实时的资源管理技术,它通过智能化的调度和优化,使得系统中的资源得到高效、公平的使用,从而提高整个系统的性能和效率。这种算法在云计算、大数据处理、分布式系统等领域有着广泛的应用前景。
单调速率调度算法 (Rate Monotonic Scheduling,RMS) 是一种静态优先级调度算法,
也是最常用的一种确定任务优先级的算法。RMS 算法是基于以下的几个假设条件。
(1)所有的任务都是周期性任务。
(2)任务的时间期限等于它的周期。
(3)任务在每个周期内的执行时间是一个常量。
(4)任务之间不进行通信,也不需要同步。
(5)任务可以在任何位置被抢占,不存在临界区的问题。
RMS 算法的基本思路:任务的优先级与它的周期表现为单调函数的关系,任务的周
期越短,优先级越高;任务的周期越长,优先级越低。
RMS 算法是一种最优调度算法:如果存在一种基于静态优先级的调度顺序,使得每个任务都能在其期限时间内完成,那么RMS 算法总能找到这样的一种可行的调度方案。当然,对于具体的某一组任务而言,这种调度方案并不一定存在。但只要存在,就能通过RMS 算法进行调度。
最早截止期限优先动态调度(EDF)算法是一种 动态优先级调度算法,它能根据需要动态地 修改 各个任务的优先级,是目前性能较好的一种调度算法。EDF 算法的基本思路是:根据任务的截止时间来确定其优先级,对于时间期限最近的任务,分配最高的优先级。当有一个新的任务处于就绪状态时,各个任务的优先级就 有可能要进行调整。与 RMS 算法一样,EDF 算法的分析也是在一系列假设的基础上进行的,它不要求系统中的任务都必须是周期任务,其他的假设条件与RMS 相同。
EDF 算法是最优的单处理器动态调度算法,其可调度上限为100%。对于给定的一组任务,只要它们的CPU 使用率小于或等于1,EDF 就能找到合适的调度顺序,使得每个 任务都能在自己的时间期限内完成。反之,如果 EDF不能满足这组任务的调度要求,则其他的调度算法也不行。
1、相互独立:任务之间没有任何的关联关系,互不干预、互不往来。唯一的相关性 就是它们都需要去竞争CPU 资源。
2、任务互斥:除了CPU 之外,这些任务还需要共享其他的一些硬件和软件资源,而 这些资源由于种种原因,在某一时刻只允许一个或几个任务去访问。因此当这些 任务在访问共享资源的时候可能会相互妨碍。
3、任务同步:任务之间存在着某种依存关系,需要协调彼此的运行步调。
4、任务通信:任务之间存在着协作与分工,需要相互传递各种数据和信息,才能完
成各自的功能。
在嵌入式操作系统当中,对于任务间的第一种关系,主要是靠调度器来进行协调。 而对于其他的几种关系,操作系统必须提供一些机制,让各个任务能够相互通信、协调各自的行为,以确保系统能够顺利、和谐地运行。
任务竞争条件是指多个任务同时请求访问共享资源的情况。
当某个任务正在访问某个资源时,其他任务必须等待,直到该任务完成对资源的访问。
实现互斥访问的条件:
1、在任何时候最多只能有一个任务位于它的临界区当中;
2、不能事先假定CPU 的个数和系统的运行速度;
3、如果某一个任务没有位于它的临界区当中,它不能妨碍其他的任务去访问临界源;
4、任何一个任务进入临界区的请求必须在有限的时间内得到满足,不能无限期地待。
任务临界区是指一个任务中访问临界资源的代码段,这些临界资源一次只允许一个任务访问。进入临界区的任务会阻止其他任务的进入,直到它完成对临界资源的访问。这样可以避免多个任务同时访问临界资源而引发的问题,如数据冲突和不一致。
任务信号量是一种实现任务间通信的机制,用于实现任务之间的同步或临界资源的互斥访问。信号量是一个非负整数,所有获取它的任务都会将该整数减一。当信号量为零时,试图获取信号量的任务将会阻塞,直到信号量被释放。信号量的值通常用于表示有效的资源数,表示剩下的可被占用的互斥资源数。
信号量是由操作系统来维护的,任务不能直接去修改它的值,只能通过初始化和两个标准原语(即 P 、V 原语)来对它进行访问。
P 原语中的字母P, 是荷兰语单词测试的首字母。它的主要功能是申请一个空闲资源,把信号量的值减1。如果成功的话,就退出原语;如果失败的话,这个任务就会被阻塞起来。
V 原语当中的字母 V, 是荷兰语单词增加的首字母。它的主要功能是释放一个被占用的资源,把信号量的值加1,如果发现有被阻塞的任务,就从中选择一个把它唤醒。
采用信号量来实现任务之间的互斥,优点有两个: 一是可以设置信号量的计数值, 从而允许多个任务同时进入临界区,如果信号量的初始值大于0,则允许多个任务同时进入临界区。如果信号量的初始值为1,则只允许一个任务进入临界区,即互斥信号量;二是当一个任务暂时无法进入临界区时,它会被阻塞起来,从而让出CPU 给其他的任务。
在一组任务当中,每个任务都占用着若干个资源,同时又在等待其他任务所占用的资源,从而造成所有任务都无法进展下去的现象,这种现象称为死锁,这一组相关的任务称为死锁任务。在死锁状态下,每个任务都动弹不得,既无法运行,也无法释放所占用的资源,它们互为因果、相互等待。
死锁的产生有四个必要条件,只有当这四个条件同时成立时,才会出现死锁。
1、互斥条件:在任何时刻,每一个资源最多只能被一个任务所使用;
2、请求和保持条件:任务在占用若干个资源的同时又可以请求新的资源;
3、不可抢占条件:任务已经占用的资源不会被强制性拿走,而必须由该任务主动放;
4、环路等待条件:存在一条由两个或多个任务所组成的环路链,其中每一个任务都在等待环路链中下一个任务所占用的资源。
除了资源的竞争之外, PV 操作使用不当也会引起死锁。
在系统中,定义了两个信号量S 和 Q, 它们的初始值都是1。两个任务T₁ 和 T₂, 假 设T₁ 先被调度执行,它顺利地通过了P(S)操作,并使S 的值变为0。假设这时发生了一 次时钟中断,任务T₂ 被调度执行。它顺利地通过了P(Q)操作,并将Q 的值变为0。接着 在执行P(S) 操作时,由于S 的值已经是0,因此T₂ 在这里被阻塞起来,并让出CPU 。然 后任务 T₁ 重新开始运行,但是当它执行到 P(Q)时,由于Q 的值已经为0,因此T₁ 也被 阻塞起来。这样一来,任务T₁ 和 T₂ 都处于阻塞状态,都在等待对方释放信号量,这就是一种死锁的状态。
实时系统中不同的任务经常需要互斥地访问共享资源。当任务试图访问资源时被正使用该资源的其他任务阻塞,可能出现优先级反转的现象,即当高优先级任务企图访问 已被某低优先级任务占有的共享资源时,高优先级任务必须等待直到低优先级任务释放它占有的资源。如果该低优先级任务又被一个或多个中等优先级任务阻塞,问题就更加 严重。由于低优先级任务得不到执行就不能访问资源、释放资源。于是低优先级任务就 以一个不确定的时间阻塞高优先级的任务,导致系统的实时性没有保障。
系统存在任务1、任务2、任务3(优先级从高到低排列)和资源R。 某时,任务1和任务2都被阻塞,任务3运行且占用资源R 。 一段时间后,任务1和任 务2相继就绪,任务1抢占任务3运行,由于申请资源R失败任务1被挂起。由于任务 2的优先级高于任务3,任务2运行。由于任务3不能运行和释放资源R, 因此任务1一直被阻塞。极端情况下,任务1永远无法运行,处于饿死状态。
PIP 协议能与任何优先级驱动的抢占式调度算法配合使用,而且不需要有关任务访问 资源情况的先验知识。优先级继承协议的执行方式是:当低优先级任务正在使用资源, 高优先级任务抢占执行后也要访问该资源时,低优先级任务将提升自身的优先级到高优 先级任务的级别,保证低优先级任务继续使用当前资源,以尽快完成访问,尽快释放占 用的资源。这样就使高优先级任务得以执行,从而减少高优先级任务被多个低优先级任 务阻塞的时间。低优先级任务在运行中,继承了高优先级任务的优先级,所以该协议被称作优先级继承协议。
由于只有高优先级任务访问正被低优先级任务使用的资源时,优先级继承才会发生, 在此之前,高优先级任务能够抢占低优先级任务并执行,所以优先级继承协议不能防止死锁,而且阻塞是可以传递的,会形成链式阻塞。另外, 优先级继承协议不能将任务所经历的阻塞时间减少到尽可能小的某个范围内。 最坏情况下, 一个需要μ个资源,并且与v 个低优先级任务冲突的任务可能被阻塞min(μ,v) 次。
优先级天花板是指当线程申请某资源时,将该线程的优先级提升到可访问这个资源的所有线程中的最高优先级。这种方法简单易行,不必进行复杂的判断,无论线程是否阻塞了高优先级线程的运行,只要线程访问共享资源都会提升线程的优先级。这种方法在实现上并不需要复杂的算法或逻辑,只需要将申请资源的任务的优先级提升到所有可能访问该资源的任务中的最高优先级即可。这种策略可以有效地避免优先级反转的情况,即低优先级的任务先于高优先级的任务执行。通过设置优先级天花板,可以确保高优先级的任务能够及时地获得所需的资源,从而保证系统的稳定性和效率。
共享内存指的是各个任务共享它们地址空间当中的某些部分,在此区域,可以任意读写和使用任意的数据结构,把它看成是一个通用的缓冲区。 一组任务向共享内存中写入数据,另一组任务从中读出数据,通过这种方式来实现它们之间的信息交换。
在有些嵌入式操作系统中,不区分系统空间和用户空间,整个系统只有一个地址空间,即物理内存空间,系统程序和各个任务都能直接对所有的内存单元进行随意地访问。在这种方式下,内存数据的共享就变得更加容易了,如图4-25所示。
消息传递指的是任务与任务之间通过发送和接收消息来交换信息。
任务之间的通信方式可以分为直接通信和间接通信两种。
(1)直接通信:在直接通信方式下,通信双方必须明确知道与之通信的对象。采用
类似下面的通信原语:
send(P,message): 发送一条消息给任务P;
receive(Q,message): 从任务Q 那里接收一条消息。如果没有收到消息,可以阻
塞起来等待消息的到来,也可以立即返回。
在通信双方之间存在一条通信链路,该链路具有如下特征:
●通信链路是自动建立的,由操作系统来维护;
●每条链路只涉及一对相互通信的任务,每对任务之间仅存在一条链路;
●通信链路可以是单向或双向的。
(2)间接通信:在间接通信方式下,通信双方不需要指出消息的来源或去向,而是
通过共享的邮箱 (mailbox) 来发送和接收消息,每个邮箱都有一个唯一的标识。采用类
似下面的通信原语:
send(A,message): 发送一条消息给邮箱A;
receive(A,message): 从邮箱A 接收一条消息。
间接通信的特点:
●对于一对任务,只有当它们共享一个公共邮箱时才能进行通信;
●一个邮箱可以被多个任务访问,每对任务也可以使用多个邮箱来通信;
●通信可以是单向或双向的。
邮箱只能存放单条消息,它提供了一种低开销的消息传递机制,其状态只有两种: 空或满。另外一种间接通信机制是消息队列。它与邮箱是类似的,但可以同时存放若干条消息,提供了一种任务间缓冲通信的方法。如图4-26所示,发送消息的任务将消息放入队列,而接收消息的任务则将消息从队列中取出。
管道即连接两个任务之间的一个打开的共享文件,专用于任务之间的数据通信。发送任务从管道的一端写入数据流,接收任务从管道的另一端 按先进先出的顺序读出数据流。管道的读写操作即为普通的文件读写操作,数据流的长度和格式没有限制。
分区调度:是指操作系统将系统资源分配给各个分区的过程。每个分区是一个独立的运行环境,拥有自己的资源和管理机制。调度器负责按照一定的策略和优先级,将处理器时间、内存空间等资源分配给各个分区。分区调度可以提高系统的并发性和响应能力,使得多个任务能够同时运行,并且能够根据任务的优先级和需求进行合理的资源分配。
分区通信:是指不同分区之间的信息交换和数据传输。由于各个分区运行在不同的环境中,因此需要进行通信以实现数据共享、任务协调等功能。分区通信可以采用不同的通信协议和机制,如共享内存、消息传递、管道通信等。通过分区通信,可以实现各个分区之间的数据传输、任务协调和同步,以及分布式系统中的进程间通信等功能。