但无论选择如何分配优先级,任务执行的顺序都由调度策略决定。调度策略确定任务实际运行的时间。
AUTOSAR OS支持两种调度策略:
2.1.1 抢占式调度
当一个Task正在运行,另一个更高优先级的任务准备运行,此时高优先级的任务会抢占执行,这也被称为任务切换,高优先级任务执行完毕后,原来的task继续执行。
与抢占式调度不同的是,如果高优先级任务准备就绪,那么它将保持准备运行状态,直到运行任务终止—它不会抢占。这意味着开始运行的非抢占式任务将始终运行到完成,然后终止。
实际上,AUTOSAR OS提供了对第三种称为协作调度的调度的支持,因为它允许非抢占式任务告诉OS何时可以抢占。我们之所以说AUTOSAR OS支持两种策略,是因为只有两种配置—第三种配置必须自己构建。
2.2 基础任务与扩展任务
当前项目中配置的除SecOC外的Task都是配置的BASIC
2.2.1 基础任务
状态:Running,Ready,Suspended。
基本任务开始、执行和终止(这通常称为单次任务模型)。只有当基本任务终止或被更高优先级的任务抢占时,它才会释放处理器。这种行为使它们非常适合嵌入式控制功能。基本任务快速有效。
2.2.2 扩展任务
扩展任务时间响应较快,但会一直占用Ram资源,相当于以空间换时间。
2.2.3 Task State
Ready、Running 、(Waiting) 、Suspended
所有任务默认的状态都是Suspended状态,在被激活后才变成Ready状态,当该任务为系统中的最高优先级任务时即转为Running状态,并开始执行任务,同时任务可能被更高优先级的任务抢占,重新变成Ready状态。
任务通过终止返回到挂起(Suspended)状态。一项任务可以在以后再次准备好,整个过程可以重复。
但是,扩展任务也可以进入Waiting状态。当扩展任务通过等待事件自动挂起时,它将从Running状态移动到Waiting状态。
当扩展任务进入Waiting状态时,操作系统将分派准备运行的最高优先级任务。设置事件后,任务将从Waiting状态移动到Ready状态。请注意,扩展任务返回到Ready状态,而不是Running状态。这是因为,在扩展任务处于Waiting状态期间,其他一些更高优先级的任务可能已被激活,然后被调度。
Event只是用于为系统事件提供指示器的操作系统对象。事件示例包括数据准备好使用或正在读取传感器值。
激活Task的方式有:调用ActivateTask() API,或Alarm到期或调度表。
2.2.4 任务优先级
AUTOSAR-OS运行任务可以享有同一优先级,同一优先级的任务以先进先出FIFO(First In First Out)的原则从Ready状态Release。
使用时需避免使用Share Priorities,实时性差,无法进行调度可行性分析。
2.2.5 队列任务激活
在大多数情况下,仅需任务处于挂起状态时激活它。事实上,AUTOSAR OS将处于就绪、运行或等待状态的任务激活视为错误情况。
但是,在某些情况下,可能需要实现一个系统,该系统必须多次激活同一任务,但连续激活之间最短的时间可能小于运行该任务所需的时间。例如,用户可能正在任务中解包CAN总线帧,并且需要处理网络上帧的瞬时突发。这意味着用户需要在运行时对任务激活进行排队。
AUTOSAR OS允许对基本任务的激活进行排队,以帮助构建此类应用程序。与AUTOSAR OS中的其他功能一样,任务队列的大小是静态配置的。必须指定任务可以挂起的最大激活次数。如果超过激活的最大次数,激活将被忽略。
2.2.6 异步任务激活
AUTOSAR OS允许别的核激活当前核的Task,而不是从任务实际运行的核激活任务。虽然这可能很有用,但可能会影响性能.因为要完全符合AUTOSAR,所有任务激活(包括SetEvent)必须阻止调用方,直到任务状态更新。在内部,操作系统必须使用内部自旋锁来协调与所属核的状态更改。这会对所有核的性能产生重大影响。
2.3 一致性类
AUTOSAR OS对什么样的功能可以一起使用有一些限制。这些被称为一致性类,用于对任务特性进行分组,以便于理解,支持标准的部分实现,并为不同的应用程序类提供可伸缩性。
2.4 最大性能与最小内存
RTA-OS在最小化目标应用程序上的代码和数据使用方面设计得非常积极。它将分析应用程序的特性,并生成一个仅包含所需特性的系统。
用户对任务特征的选择对最终应用的规模和速度有重大影响。“没有免费的午餐”,因此,当用户向应用程序中添加使用更高级任务类型的任务时,系统将不可避免地变得稍大、稍慢。
为系统运行更有效率,同时在使用过程中应尽量使用BCC1,尽量少使用BCC2及扩展任务。
因为:同优先级的,队列等待任务;扩展任务需要等待都会影响运行效率。
3. Task 配置
包括:name、Priority、Scheduling、Activation及Autostart
优先级:0是最低优先级,
调度:任务可以完全抢占或非抢占地运行。一般来说,为了获得最佳的应用程序性能,应该选择完全抢占式调度而不是非抢占式调度。
激活:在Ready状态下可以排队的最大任务激活数。对于BCC1、ECC1和ECC2任务,激活次数始终为一次。这意味着这些类型的任务只有在处于挂起状态时才能激活。在未挂起的情况下,尝试激活此类任务将导致错误。大于1的值表示操作系统将对激活进行排队,任务结束时再次激活。
3.1 调度原则
低优先级的任务会被高优先级任务抢占;
对于协作式调度,低优先级的任务可为低优先级声明一段时间,在该时间段内不能被高优先级任务抢占,但仍可能被中断打断。
通常,使用非抢占性任务的系统的响应性比抢占性运行的系统差。
经常会发现,不必使用不可抢占的任务,因为用户可以使用其他更合适的方法来实现更灵敏的系统。可使用的手段包括:
•使用标准资源序列化对数据或设备的访问。
•使用内部资源精确指定哪些其他任务不会导致抢占。
3.2 队列激活(仅针对BCC2)
在大多数情况下,用户将仅在任务处于挂起状态时激活它。但是,用户可能需要实现一个系统,在该系统中,同一任务必须被多次激活,并且连续激活之间的最短时间小于运行该任务所需的时间。
如果发生这种情况,用户将在任务处于就绪状态或正在运行状态时激活该任务。这意味着激活将丢失。要防止激活丢失,必须指定任务所需的最大多次激活次数。
根据AUTOSAR OS标准,此功能仅适用于基本任务。不能指定 扩展任务的多个激活。
当指定多个激活时,RTA-OS会自动识别任务为BCC2。构建应用程序时,RTA-OS将计算每个BCC2任务所需的多激活队列的最大大小。
当BCC2任务共享优先级时,RTA-OS使用FIFO队列来保存挂起的激活。如果BCC2任务在AUTOSAR OS应用程序中具有唯一的优先级,则RTA-OS会自动优化排队策略,使其达到最佳优先级激活。计数激活比FIFO激活更有效,应尽可能使用。
3.3 自动开始的任务
自动启动主要用于启动Waiting Event的扩展任务,因为它不需要编写代码来激活任务。
4. 堆栈管理
RTA-OS使用单栈模型,即所有的Task、ISRS在同一个堆栈区域上运行。
在单堆栈模型中,堆栈大小与系统中优先级的数量成比例,而不是与任务/ISR的数量成比例。这意味着,直接或通过共享内部资源,或通过被配置为非先发制人,共享优先级的任务永远不会同时出现在堆栈上。在硬件上共享优先级的ISR也是如此。这意味着用户可以通过简单的配置更改来交换系统响应性,即任务或ISR完成所需的时间,以换取堆栈空间。
单堆栈模型还显著简化了链接时的堆栈空间分配,因为用户只需为整个系统堆栈分配单个内存部分,就像根本不使用操作系统一样。
4.1 扩展任务的堆栈管理
RTA-OS独特地扩展了单堆栈模型,在不影响基本任务性能的情况下为扩展任务提供支持。
在RTA-OS中,扩展任务的生命周期如下:
Suspended→Ready:任务被添加到Ready 序列
Ready→Running:任务已调度,但与内容位于堆栈顶部的基本任务不同,内容位于堆栈空间中,所有低优先级任务的预先计算的最坏情况抢占深度处。
Running→Ready:扩展任务被抢占。如果抢占任务是一个基本任务,那么它会像往常一样在堆栈顶部调度;如果抢占任务是一个扩展任务,那么它将按照预先计算的所有低优先级任务的最坏情况抢占深度进行调度。
Running→Waiting:任务的等待事件堆栈内容(包括操作系统上下文、本地数据、函数调用的堆栈帧等)保存到内部操作系统缓冲区中
Waiting→Ready:任务添加到Ready 队列
4.2 强制堆栈信息
计算出的最坏情况分派点定义了需要启动扩展任务的字节数,该字节数与调用StartOS()时堆栈指针的地址有关。这些偏移量作为ROM数据存储在扩展任务控制块中,并在运行时添加到堆栈的基址。
4.3 指定任务分配Task
在仅包含基本任务的系统中,除非用户正在进行堆栈监控,否则无需告知RTA-OS任何堆栈分配。只需在链接器/定位器中为应用程序分配足够大的堆栈部分。这是单堆栈体系结构的优点之一。
对于使用扩展任务的应用程序,用户可以像以前一样分配Linker部分,但用户还必须为RTA-OS中每个优先级低于最高优先级扩展任务的任务分配堆栈,即使它们是基本任务。RTA-OS使用堆栈分配信息为每个离线扩展任务计算最坏情况下的抢占点。
虽然RTA-OS使用单堆栈模型,但在某些端口上,这并不一定意味着只使用一个物理堆栈。可能是编译器或硬件自动将数据强制到不同的堆栈上。例如,TriCore设备使用CSA内存快速保存调用context。
即使有多个物理堆栈,RTA-OS仍然提供了单堆栈体系结构的好处——当任务和/或ISR共享优先级时,每个物理堆栈上所需的堆栈空间可以重叠。但是,要使堆栈分配正常工作,需要指定每个堆栈上所需的空间。如果用户配置,RTA-OS将要求用户提供多个堆栈值
4.4 处理堆栈溢出
如果用户提供给RTA-OS的堆栈分配太小(运行时错误的潜在来源),有三种错误情况可能发生:
1.当RTA-OS尝试调度扩展任务时,堆栈指针的当前值高于计算的最坏情况调度点,因此扩展任务无法启动。这意味着堆栈上的一个(或多个)低优先级任务占用了太多空间。
2.扩展任务无法从等待状态恢复,因为堆栈指针高于它应该的值。当为扩展任务正在等待的事件调用SetEvent()并且扩展任务现在是系统中优先级最高的任务时,可能会发生这种情况。
3.扩展任务无法进入等待状态,因为任务正在使用的堆栈的当前数量大于配置的“WaitEvent()堆栈”的大小。
当RTA-OS检测到扩展任务堆栈管理出现问题时,它将调用ShutdownOS(),错误代码为E_OS_STACKFAULT。如果要调试该问题,则可以启用堆栈故障Hook。
配置后,RTA-OS将在堆栈故障发生时调用用户提供的回调OS_Cbk_StackOverflowHook(),而不是ShutdownOS()。回调传递了两个参数:溢出的字节数、溢出的原因。
5. 任务的控制
5.1任务执行
任务类似于C函数,在RTA-OS调用它们时实现某种形式的系统功能。
当任务开始运行时,从任务输入函数开始执行。任务输入函数是使用示例4.2中的C语法编写的。
注意,基本任务是一次性完成的。这意味着他们从固定的任务入口点开始执行,并在完成时终止。
5.2 任务激活
任务激活后变为Ready状态,当该任务的优先级为所有Ready状态的任务及当前处于Running的任务中最高时,开始(或抢占)变成Running状态。
任务每次被激活后会运行一次,当激活的次数超过配置次数时会报E_OS_LIMIT错误。
任务可以通过Tasks或二类中断激活。
5.2.1 直接激活
可以通过多种不同的方式激活任务。任务激活的基本机制是ActivateTask()API调用,它直接激活任务。ActivateTask(TaskID)调用将命名任务置于就绪状态。ChainTask(TaskID)终止当前正在运行的任务(参见第4.11节),并将激活指定任务置于就绪状态。
#include <Os.h>
TASK(Task1) {
/* Task1 functionality. */
ActivateTask(Task2);
TerminateTask();
}
TASK(Task2) {
/* Task2 functionality. */
ActivateTask(Task3);
TerminateTask();
}
TASK(Task3) {
/* Task3 functionality. */
TerminateTask();
}
5.2.2 间接激活
通过Alarm激活或通过调度表激活。
通过Alarm激活:对于系统中的每个Alarm,可以指定一个任务,该任务在每次Alarm过期时激活。
调度表激活:对于系统中的每个调度表,可以指定在表上的一个或多个到期点激活的任务。
5.3 任务执行顺序控制
许多情况下,需要控制任务执行的顺序,即某个任务的执行需要基于前一个任务执行的结果。时序的控制可以通过如下三种方式:
•直接激活链;
•优先级;
•不可抢占的任务。
5.3.1 直接激活链
5.3.2 通过任务优先级
将需要优先执行的任务设置更高的优先级,在执行时即可按抢占式完成。
5.4 RTA-OS中的合作式调度
#include <Os.h>
TASK(Cooperative){
Function1();
Schedule();/* Allow preemption */
Function2();
Schedule();/* Allow preemption */
Function3();
Schedule();/* Allow preemption */
Function4();
TerminateTask();
}
注:该应用形式,在抢占式调度中Schedule();没有意义。
5.5.任务终止
在AUTOSAR操作系统中终止的任务必须进行API调用,以告知操作系统正在发生这种情况。AUTOSAR OS标准定义了两个用于任务终止的API调用。其中之一必须用于终止任何任务。这些API调用是:
• TerminateTask()
• ChainTask(TaskID)
当任务完成时,它必须进行其中一个API调用。这确保RTA-OS可以正确地安排准备运行的下一个任务。
TerminateTask()强制调用任务进入挂起状态。RTA-OS将在Ready状态下运行下一个优先级最高的任务。
ChainTask(TaskID)终止调用任务并激活任务TaskID。因此,该API类似于执行TerminateTask(),然后立即执行ActivateTask(TaskID)。链接任务会将命名任务置于就绪状态。
5.5.1 RTA-OS中的终端优化
AUTOSAR OS标准允许任务在任何时候调用任务终止API调用,包括在一组深度嵌套的函数调用中。这是一种糟糕的编程实践,相当于使用goto。在运行时,RTA-OS必须存储信息,以便在任务终止于entry函数以外的其他位置时清除堆栈。这通常是使用setjmp/longjmp对完成的。
然而,单堆栈体系结构的一个关键好处是,在其entry函数中终止的任务可以简单地返回TerminateTask()而无需执行任何操作。
RTA-OS允许用户使用快速终止优化(OptimizationsÔfast Terminate)利用良好的应用程序设计。当所有执行TerminateTask()或ChainTask()API的任务仅在其entry函数中执行时,可以启用此优化。优化告诉RTA-OS不要生成代码来保存不必要的上下文,从而节省堆栈空间。
5.6 任务的Idle机制
在任何抢占式OS中,当没有Task或中断运行时也必须有些事情要做。在AUTOSAR OS中是通过Idle 机制实现。在RTA-OS中当没有Task或中断执行时,系统将处于繁忙的等待循环中。
用户可以通过声明一个名为Os_Cbk_idle的回调来提供自己的空闲机制实现,从而覆盖默认行为。
Os_Cbk_idle的行为较普通Task外有如下不同:
-
不能被激活
-
不能被终止
-
不能被链(嵌套)调用
-
不能使用内部资源
Os_Cbk_Idle的优先级最低,即当没有任何任何及中断运行时才会Ready to Run。
Os_Cbk_Idle在退出时返回一个布尔值,告诉RTA-Os是否再次调用Os_Cbk_Idle。当返回TRUE时,RTA-OS将立即再次调用Os_Cbk_Idle。当返回FALSE时,RTA-OS停止调用Os_Cbk_Idle并进入繁忙等待循环的默认行为。
6. 任务前与任务后Hooks
假设用户需要在每个任务开始之前和/或在每个任务结束之后执行一些代码,例如,以提供执行跟踪。用户可以使用AUTOSAR OS提供的PreTask和PostTask Hook来实现这一点。
每当任务进入运行状态时,RTA-OS就会调用预任务Hook。这意味着在抢占后,每当任务恢复时,也会调用预任务Hook。
每当任务移出运行状态时,RTA-OS就会调用PostTaskHook。任务终止时,每次任务被抢占时,都会调用PostTaskHook。
需要配置后才能使用该功能,用于在运行时对应用程序进行配置。
参考资料:
《RTA-OS User Guide》
————————————————
版权声明:本文为CSDN博主「initiallizer」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文始发于微信公众号(汽车电子与软件):RTA-OS系列介绍-Task