前言
本系列文章将以RTA-OS为例详细介绍AUTOSAR OS标准及概念,并分享实际使用的一些案例,本文为符合AUTOSAR标准的RTA-OS--Task介绍。
正文
2.任务Task
必须同时执行许多不同活动的系统称为并发。这些活动可能包含一些软件部分,因此提供这些活动的程序必须同时执行。这些程序必须在任何必要的时候进行合作,例如,当它们需要共享数据时。
实时系统中的每个并发活动都由一个任务来表示。大多数应用程序代码都存在于任务中。如果有许多必须同时执行的任务,那么将需要提供一种允许并发性的方法。其中一种方法是为每个任务都有一个单独的处理器。可以使用并行计算机,但这个解决方案对许多应用程序来说太昂贵了。
实现并发行为的一个更经济有效的方法是在单个处理器上一次运行一个任务。然后可以在任务之间切换,以便它们看起来同时执行。
2.1 调度Scheduling
RTA-OS提供了一个调度器,它根据在配置时分配的固定优先级在任务之间切换。优先级只是反映了任务的相对紧迫性。可以使用许多方案来分配任务的优先级,常见方案有:
Deadline Monotonic Assignment: 更高的优先级被分配给最后期限较短的任务。
Rate Monotonic Assignment: 更高的优先级被分配给需要更频繁地运行的任务。
无论选择分配优先级,任务执行的顺序由调度策略决定。调度策略决定任务何时实际运行。
AUTOSAR操作系统支持两种调度策略:
1.抢占式调度Preemptive Scheduling.
固定优先级抢占式调度算法很简单:运行准备运行的最高优先级任务。如果任务正在运行,而优先级较高的任务准备运行,则优先级较高的任务优先于正在运行的任务。这叫做任务切换。当高优先级任务完成后,抢占任务恢复。
对于所有任务都需要在运行时满足其最后期限的系统,抢占式的调度是最有效的调度策略,并将保证任务被激活(准备运行)和终止之间的最短时间。这个时间被称为对该任务的响应时间。抢占式调度的系统需要考虑抢占对共享数据的影响,并且可能需要引入并发控制机制。
图2.1 任务的抢占式调度
2.非抢占式调度 Non-Preemptive scheduling.
操作系统运行优先级已准备运行的最高任务,和抢占式调度一样。然而,抢占式调度不同的是,如果一个更高优先级的任务准备好了,那么它就会一直准备好运行,直到正在运行的任务终止——它不会抢占正在允许的低优先级的任务。这意味着,开始运行的非抢占式任务将始终运行到完成,然后终止。
非抢占式调度导致系统比抢占式调度的响应性更低(即任务通常有更长的响应时间),但系统不需要担心访问共享数据时出现的并发问题,因为调度模型不允许并发访问共享数据。
实际上,AUTOSAR OS提供了第三种类型的调度支持,因为它允许非抢占任务告诉操作系统什么时候可以被抢占。我们说AUTOSAR操作系统支持2个策略的原因是只有两个配置——第三个配置,必须自己构建。
图2.2 任务的非抢占式调度
3.协同调度Cooperative scheduling.
操作系统将运行优先级最高的准备运行的任务。如果一个更高优先级的任务准备好了,那么它会一直准备运行直到:正在运行的任务终止(就像非抢占式调度);或者正在运行的任务调用Schedule() API来告诉操作系统它可以被抢占。当Schedule()调用时,高优先级任务抢占正在运行的任务,任务切换就发生了(就像抢占调度一样)。当高优先级任务完成后,被抢占的任务恢复。
通过仔细的设计,协同模式提供的系统虽然不像完全抢占式系统那样响应迅速,但不会像非抢占式调度那样缺乏响应能力。
图2.3 任务的协同调度
对于所有这些类型的调度,重要的是要意识到任何任务,无论是否抢占人,都可以被中断服务例程中断(抢占)。
RTAOS支持两种类型的Task:
l基本任务Basic Task
基本任务的开始、执行和结束(通常称为单次任务模式)。一个基本任务只有在它被终止或被一个更高优先级的任务抢占时才释放处理器。这种行为使它们非常适合嵌入式控制功能。基本任务是快速高效的。
l扩展任务Extended Task
扩展任务启动、执行、等待事件和(可选)终止。扩展任务在执行期间自动挂起自身的能力为任务提供了一种具有同步点的方法。这个特性使得扩展任务比基本任务更适合需要执行中间同步的功能(例如,等待用户交互)。
l挂起Suspended
l准备Ready
l允许Running
扩展任务可以在等待事件时进入一个额外的状态:
l等待Waiting
图2.4展示了任务3或者4状态模型。
所有任务的默认状态是挂起。任务通过激活过程进入就绪状态。重要的是要理解激活并不会导致任务运行——它只是让任务准备好运行。激活可以通过多种方式发生,例如在代码中调用ActivateTask() API,或者作为某些触发器的结果,例如警报(Alarm)到期或调度表到期点( schedule table expiry point)。
当一个任务成为系统中优先级最高的任务时,RTA-OS将该任务移至运行状态,并在任务中的第一条语句开始执行任务。这通常被称为调度任务。一个任务可能在执行过程中被其他准备就绪的高优先级任务抢占。
图2.4 任务状态模型
如果有更高优先级的任务准备运行,则当前正在执行的任务将被抢占,并从运行状态移至就绪状态。这意味着在同一时间内只能有一个任务处于运行状态。
任务终止后返回挂起状态。任务可以在以后再次准备好,整个过程可以重复。
基本任务和扩展任务在准备、运行和挂起状态方面表现相同。但是,扩展任务也可以进入等待状态。当扩展任务通过等待事件自动挂起自己时,它将从运行状态转移到等待状态。
事件只是一个OS对象,用于为系统事件提供指示器。事件的例子包括数据准备使用或传感器值正在读取。
当扩展任务进入等待状态时,操作系统将分派准备运行的最高优先级任务。当设置事件时,任务将从等待状态移动到准备状态。注意,扩展任务返回到就绪状态,而不是运行状态。这是因为,在扩展任务处于等待状态期间,其他一些更高优先级的任务可能已经被激活,然后被分派。
AUTOSAR OS允许任务共享优先级。当任务具有相同优先级时,具有共享优先级的每个任务将相互排斥运行。这意味着如果一个任务正在运行,那么它的执行将与所有其他具有相同优先级的任务序列化。
当任务共享优先级时,它们将按照先进先出(FIFO)的顺序从就绪状态释放。
Note:当共享优先级和排队任务激活一起使用时,RTA-OS在优先级级别上维护一个内部队列。如果想要一个快速高效的操作系统,你应该避免这种类型的配置。
如果需要序列化一组任务的执行,那么最好使用惟一的优先级和AUTOSAR OS的内部资源来实现,而不是共享任务优先级。使用内部资源保证了序列化,就像共享优先级一样,任务优先级的唯一性意味着当多个任务同时就绪时,操作系统有一个静态定义的分派顺序。
Note:在任务之间共享优先级是一种糟糕的实时编程实践,因为它会阻止您在系统上执行可调度性分析。这是因为,在一般情况下,共享优先级使得任务的释放点(即测量响应时间的点)在计算上无法计算。如果不可能计算出何时发布,那么就不可能决定任务是否会在截止日期前完成!
在大多数情况下,只能在任务处于挂起状态时激活它。AUTOSAR OS在任务处于就绪、运行或等待状态时将其激活视为错误情况。
然而,在某些情况下,我们可能需要实现这样一个系统:相同的任务必须多次激活,但连续激活之间的最短时间可能小于运行任务所需的时间。例如,您可能正在一个任务中解包CAN总线帧,并且需要处理网络上帧的瞬时爆发( transient bursting)。
这意味着我们需要在运行时排队等待任务激活。AUTOSAR OS操作系统允许排队激活基本任务,以帮助构建这类应用程序。与自动共享操作系统中的其他东西一样,任务队列的大小也是静态配置的。我们必须指定该任务可以挂起的最大激活数。
如果在尝试激活任务时队列已满,则这将作为错误处理,激活将被忽略。
当然,您可能会有共享优先级并使用队列激活的任务。在这种情况下,任务按FIFO顺序在一个队列中排队,其长度等于共享相同优先级的每个任务的队列长度之和。但是,每个任务只能使用它自己的条目数量。
AUTOSAR OS允许从内核激活任务,而不是任务实际运行的内核。虽然这可能很有用,但可能会有性能影响,因为要完全兼容AUTOSAR,所有任务激活(包括SetEvent)必须阻塞调用方,直到任务状态更新。在内部,操作系统必须使用内部自旋锁与拥有的核协调状态更改。这可能会对所有核的性能产生重大影响。
RTA-OS提供了一个OS选项异步任务激活,它改变了激活的行为,这样就不会阻塞内核,而是将消息发送到拥有要激活任务的内核上的队列。拥有的核心是执行任务的实际激活的核心,因此它是唯一更改任务状态的核心。不使用任务自旋锁,因为不需要保护任何状态。
任务激活队列的大小默认设置为10。如果该队列已满,则返回错误码E_OS_SYS_XCORE_QFULL。队列大小可以使用AsyncQ OS选项更改。
Note: 当选择异步TASK激活时,E_OS_LIMIT指示会在拥有该任务的核上发出,而不是在激活它的核上。
我们现在知道,任务可以:
l基本的或扩展的
l可以共享优先级
l可以排队激活
然而,AUTOSAR操作系统对一起使用的特性进行了一些限制。这些特性被称为一致性类,用于对任务特征进行分组,以便于理解,支持标准的部分实现,并为不同类别的应用程序提供了可伸缩性。
AUTOSAR操作系统有四个一致性类:
BCC1 -基本任务,唯一的优先级和没有排队的激活( Basic tasks, unique priority and no queued activation).
BCC2 - 基本任务、共享优先级和/或排队激活(Basic tasks, shared priorities and/or queued activation).
ECC1 - 扩展的任务,唯一的优先级和没有排队的激活。ECC1任务类似于BCC1任务,但它可以等待事件(Extended tasks, unique priority and no queued activation. An ECC1 task is like
a BCC1 task, but it can wait on events.)
ECC2 - 扩展的任务,共享的优先级和没有排队的激活。请注意,与BCC2任务不同,ECC2任务不能排队激活(Extended tasks, shared priorities and no queued activation. Note that, unlike
BCC2 tasks, ECC2 tasks cannot queue activations.)
下表给出了可以在不同类型的AUTOSAR操作系统中使用的任务类型的快速摘要:
每个一致性级别都需要更多的资源——BCC1的系统将比ECC2的系统更快更小。您不需要关心使用哪个一致性类- RTA-OS支持所有一致性类,并将从您的操作系统配置中计算一致性类。
1但仅针对ECC2系统中的基本任务。扩展任务的激活不能被排队。
RTA-OS被设计成非常积极地最小化目标应用程序上的代码和数据使用。它将分析应用程序的特性,并生成一个只包含所需特性的系统。
任务特征的选择对最终应用程序的大小和速度有很大影响。所以当你在应用程序中添加使用更高级任务类型的任务时,系统将不可避免地变得稍微大一点和慢一点。
具有一个或多个BCC2任务的系统比仅具有BCC1任务的系统具有更大的开销。一个没有共享优先级的系统,即使允许进行多个激活,也将比一个具有共享优先级的系统更有效。
带有ECC1任务的系统开销更大,而具有一个或多个ECC2任务的系统开销是最大的。
为了使RTA-OS尽可能高效,您应该只使用基本任务,而不是共享优先级。
与您可能见过的其他实时操作系统不同,AU TOSAR OS(因此,RTA-OS)中的任务是静态定义的。
这种技术被广泛使用,因为它节省了RAM和执行时间。
任务不能动态创建或销毁。关于任务的大部分信息可以离线计算,允许它存储在ROM中。
RTA-OS支持的最大任务数取决于您的端口,您应该参考目标/编译器端口指南了解更多细节。
对于所有端口,RTA-OS可以提供一个高度优化的系统,如果你限制你的任务数量到你的微控制器的本机字大小。
在配置任务属性时,您很可能会使用rtaoscfg配置工具。图2.5显示了任务配置条目。
图2.5 任务配置
一个AUTOSAR任务有5个属性:
Name. 该名称用于引用或提供要用来实现任务功能的C代码的句柄。
Priority. 调度程序使用优先级来确定任务何时运行。优先值不能动态更改。在RTA-OS中,0是最低的任务优先级。更高的任务优先级由更大的整数表示。任务可以共享优先级,但如果您正在构建一个实时系统,那么就不应该这样做,因为它不能被分析。
Scheduling. 任务可以完全抢先运行,也可以非抢先运行。一般来说,为了获得最佳的应用程序性能,应该选择完全抢占式调度而不是非抢占式调度。
Activations. 可在就绪状态下排队的任务激活的最大数量。对于一个BCC1、ECC1和ECC2任务,激活的次数为1。这意味着这些类型的任务只有在处于挂起状态时才能被激活。任何尝试在未挂起的情况下激活这样的任务都会导致错误。大于1的值表示操作系统将排队激活(例如平滑应用程序中的瞬时峰值负载)。
Autostart. 这将控制在启动操作系统时是否自动启动任务。
Note: 可以为每个目标定义的任务的数量是固定的(通常是256或1024,这取决于目标处理器)。目标的目标/编译器端口指南将包含更多的信息。
完全可抢占的任务可以被更高优先级的任务抢占。这意味着当一个更高优先级的任务准备好运行时,它将优先运行。
可以通过在配置时将任务声明为不可抢占来防止其被抢占。声明为非抢占的任务不能被其他任务抢占。当一个非抢占任务移动到运行状态时,它将运行到完成,然后终止(除非它进行Schedule()调用)。因此,使任务具有非抢占性意味着,如果低优先级任务在高优先级任务之前启动,那么在低优先级任务运行期间,高优先级任务将被阻止执行。这叫做阻塞。通常情况下,使用非抢占式任务的系统比抢先式运行的系统响应速度要慢。
即使一个任务不是可抢占的,它仍然可以被ISR中断。
使用不可抢占的任务是不必要的,因为有其他更合适的方法可以用来实现相同的效果。如果使用这些其他技术,通常会产生一个响应性更强的系统。稍后将介绍更多关于这些技巧的内容,其中包括:
l使用标准资源来序列化对数据或设备的访问。
l使用内部资源来精确地指定哪些其他任务不能导致抢占。
在大多数情况下,只有在任务处于挂起状态时才会激活它。但是,可能需要实现一个系统,其中同一任务必须被激活多次,并且连续激活之间的最短时间小于运行任务所需的时间。
如果发生这种情况,将在任务处于就绪状态或运行状态时激活该任务。这意味着激活将会丢失。
要防止失去激活,必须指定任务所需的多次激活的最大激活数。
Note: 根据AUTOSAR操作系统标准,此功能仅可用于基本任务。您不能为扩展的任务指定多个激活。
您将使用rtaoscfg指定同时激活任务的最大数量。图2.6显示,对于本例中的任务,最大激活次数已设置为20。
图2.6 指定排队激活的数量
当指定多个激活时,RTA-OS会自动识别该任务是BCC2。在构建应用程序时,RTA-OS将计算每个BCC2任务所需的多个激活队列的最大大小。
当BCC2任务共享优先级时,RTA-OS使用FIFO队列来保存挂起的激活。如果一个BCC2任务在您的AUTOSAR OS应用程序中具有唯一的优先级,那么RTA-OS将自动优化排队策略以计数激活。计数激活法比先进先出激活法效率高得多,应在任何可能的地方使用。
任务可以自动启动,这意味着当操作系统启动时,它们将在StartOS()期间自动激活。
对于启动、运行然后终止的基本任务,自动启动任务将使它只运行一次,然后才返回挂起状态(在那里它可以再次被激活)。
自动启动主要用于启动等待事件的扩展任务,因为它不需要编写代码来激活任务。
Rtaoscfg可用于指定任务仅在特定应用程序模式下自动激活,选择相关的应用程序模式并选择您希望自动激活的任务。
在图2.7中,TaskD在OSDEFAULTAPPMODE和ServiceMode应用程序模式下是自动启动的,而在LimpHomeMode和NormalOperatingMode下不是自动启动的。
图2.7 配置自动启动的任务
RTA-OS使用单堆栈模型,这意味着所有任务和ISR都运行在单个堆栈上。单个堆栈就是应用程序的C堆栈。
当任务运行时,它的堆栈使用量会正常增长和减少。当任务被抢占时,更高优先级任务的堆栈使用将继续在同一堆栈上进行(就像标准函数调用一样)。当任务终止时,它所使用的堆栈空间将被回收,然后重新用于运行下一个最高优先级的任务(同样,就像标准函数调用一样)。图2.8显示了单个堆栈在声明、抢占和终止任务时的行为。
图2.8 单堆栈行为
在单堆栈模型中,堆栈大小与系统中优先级的数量成正比,而不是任务/ ISR的数量。这意味着共享优先级的任务,无论是直接共享,还是通过共享内部资源,或者通过配置为非抢占,都不能同时在堆栈上。在硬件上共享优先级的ISR也是如此。这意味着您可以通过简单地更改配置来交换系统响应性(即任务或ISR完成所需的时间),以换取堆栈空间。