本书的原著为:《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》,讲解的是嵌入式系统设计模式,是一本不可多得的好书。
本系列描述我对书中内容的理解。本文章描述嵌入式并发和资源管理模式之二:循环执行模式。
循环执行模式
(Cyclic Executive Pattern) 是一种 调度模式
。在计算机系统中,调度模式指的是对任务、进程或线程进行调度时所采用的一种或多种特定的策略或方法。这些模式旨在优化资源利用率、提高系统性能、确保实时响应等。
循环执行模式在调度多任务时具备的优势是——简单。然而,该模式在应对紧急事件时不够灵活。尽管如此,循环执行模式确保了调度的公平性,使得所有任务都有均等的机会得以运行。虽然从响应性的视角来看,它并非最佳选择,但循环执行模式在可调度性分析方面表现出色,这个模式可以很容易的分析 可调度性
。
可调度性:如果一个任务集中的所有任务都能按时完成,那么这个任务集就被称为是可调度的。
具体来说,每个任务的截止时间必须大于或等于所有任务在最坏情况下执行时间的总和,再加上循环调度所产生的开销。如果我们假设每个任务的截止时间恰好等于一个完整任务执行周期的时长,那么如下所示的公式给出了任务集中可调度的必要条件:
D
i
≥
∑
j
=
1
n
C
j
+
K
D_{i} ≥ \sum_{j=1}^n C_{j} + K
Di≥j=1∑nCj+K
其中:
- Di 是任务 i 的截止时间
- Cj 是任务 j 的最坏执行时间
- K 是循环执行的开销,包括任务调用和返回的开销。
摘要
循环执行模式主要应用于两种场景:
- 在规模较小的嵌入式应用程序中,该模式能够支持多个任务以伪并发的方式运行,而无需引入复杂的调度程序或实时操作系统(RTOS)所带来的额外开销。
- 在高安全性的系统中,由于循环执行模式易于进行认证,因此它在航空电子和飞行管理系统等领域得到了广泛应用。
在循环执行模式中,调度程序采用简单的循环结构,按顺序逐个调用每个任务。每个任务实质上只是调度程序调用的一个函数,该函数在执行完毕后即返回,等待下一次被调用的机会。
问题
许多嵌入式系统是微型应用程序,它们的内存和时间限制非常严格,甚至无法承载即便是经过高度优化的微型内核 RTOS。循环执行模式为实现此目标提供了一种资源较少的方法。
模式结构
模式结构图如下所示:
循环执行器
包含一个名为 controlLoop()
的函数,该函数的主要职责是循环地调用每个任务线程的 run()
函数。这些 run()
函数都比较简短,一旦开始执行,它们就会一直运行到函数结束。
模式详情
抽象的循环执行线程
通过声明 run()
函数为线程提供了标准接口。循环执行器
将调用这个函数来执行任务。当函数执行完成时,循环执行器将运行列表中的下一个任务。
循环执行器
这个 类
包含无限循环结构,依次运行各个任务。另外,循环执行器还包含全局堆栈和静态数据,这些数据由任务或调度程序本身所需。
该模式的一种变体是 时间触发的循环执行器
。在这种变体中,循环执行器将设置并使用 循环定时器
来启动任务列表的任务。
循环定时器
循环定时器是可选的。常见的循环执行器并不使用循环定时器。如果使用循环定时器,定时器在到期时可以触发中断。或者更简单地,定时器到期时可以让 hasElapsed()
函数返回 TRUE(非零值)。随后,循环调度器会调用 start()
函数来再次启动循环定时器。
具体的循环执行线程
具体的任务实现,每个任务都有自己的 run()
函数。
效果
这种模式的主要好处是其简单,调度程序很难出错。缺点是,该模式对于高紧急事件明显反应较慢,这使得该模式不适合具有高响应性要求的应用程序。该模式的另一个好处是它在所需资源方面非常轻量级,因此非常适合小内存的设备。
使用循环执行模式的一个主要缺点是线程间的交互变得相对复杂。在这种模式下,如果一个任务需要另一个任务的输出数据,那么必须将这些数据存储在全局变量或共享内存中,以确保相关任务在需要时访问这些数据。
另一方面,由于不存在抢占机制,不受控制的阻塞通常不会发生。由阻塞引起的死锁只能由错误引起,比如一个行为不当的任务,该任务永远不会将控制权返回给循环执行体,从而导致整个系统挂起。
在抢占式调度系统中,即使一个任务失败或行为不当,其他任务通常也能够继续运行,除非该任务禁用了任务切换。然而,在循环执行模式中,一个任务的失败可能会导致整个系统停滞不前,直到问题得到解决。因此,在使用循环执行模式时,需要格外注意任务的设计和同步机制的实现,以确保系统的稳定性和可靠性。
实现策略
这种模式的实现几乎简单得不能再简单。在大多数情况下,循环执行器可能只是系统的 main()
函数和相关的全局数据。在其他情况下,循环执行器可能是一个函数,在 main()
函数中调用。
相关模式
由于该模式的响应性明显不是最优的,因此它经常与一些中断服务例程一起用于处理高紧急事件。将 中断模式
与 循环执行模式
一起使用的另一个用途是使用 时间触发的循环执行器
来实现周期计时器响应。
循环执行模式没有解决任务之间的数据共享问题。循环执行模式虽然定义了可用于数据共享的全局数据结构,但为了达到更好的数据共享效果,可以混合使用其他模式(比如后续文章要讲的 任务协调模式
)。由于没有抢占,对全局数据的访问不需要保护。
实例
见原书。
读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)