符合AUTOSAR标准的RTA-OS--Task详解_快讯

2023-04-17 08:26:56 来源:面包芯语

前言


【资料图】

本系列文章将以RTA-OS为例详细介绍AUTOSAR OS标准及概念,并分享实际使用的一些案例,本文为符合AUTOSAR标准的RTA-OS--Task介绍。

符合AUTOSAR标准的RTA-OS --功能简介

正文

Example 2.1: Minimum recommended Os_Cbk_StackOverrunHook()

当配置了Os_Cbk_StackOverrunHook(),当发生堆栈故障时,RTA-OS将调用用户提供的回调Os_Cbk_StackOverrunHook(),而不是关闭OS()。该回调被传递了两个参数:

1.溢出数的字节数

2.溢出原因

对于未启用堆栈监视的扩展任务系统,溢出可以是以下情况之一:

•OS_ECC_START—扩展任务无法启动,因为当前堆栈指针超过了构建时计算的最坏情况分派点。此故障的原因是一个(或多个)低优先级任务超过了配置的堆栈分配。要解决这个问题,您需要确定哪个任务出错了。栈监控和测量章节解释了如何使用RTA-OS的堆栈监视特性来做到这一点。

•OS_ECC_RESUME—扩展任务不能从等待中恢复,因为当前堆栈指针超过了构建时计算的最坏情况分派点。此故障的原因是一个(或多个)低优先级任务超过了配置的堆栈分配。

要解决这个问题,您需要确定哪个任务出错了。栈监控和测量章节解释了如何使用RTA-OS的堆栈监视特性来做到这一点。

•OS_ECC_WAIT—扩展任务使用的堆栈空间超过WaitEvent()设置的堆栈大小,无法进入等待状态。要解决这个问题,您应该至少将WaitEvent()堆栈大小增加到溢出参数所指示的字节数。

2.7 实现任务Implementing Tasks

任务类似于C函数,当它们被RTA-OS调用时,它会实现某种形式的系统功能。

Note: 不需要为任务输入功能提供任何C函数原型。这些都是通过RTA-OS生成的Os.h头文件提供的。

当任务开始运行时,将从任务输入函数开始执行。任务输入函数是使用示例2.2中的C语法编写的。

请记住,基本的任务是一次性的。这意味着它们从固定的任务入口点执行,并在完成后终止。

示例2.3显示了一个名为BCC_Task的基本任务的代码。

Example 2.3: A Basic Task

现在,将示例2.3中的示例与示例2.4进行比较。示例2.4显示,扩展的任务不一定需要终止,并且可以保持在一个循环中等待事件。

Example 2.4: Extended Task Waiting for Events

2.8 激活任务Activating Tasks

任务只有激活后才能运行。激活可以将任务从挂起状态移动到就绪状态,也可以将另一个条目添加到就绪任务队列(如果任务支持多个激活)。该任务将为每个激活运行一次。

超过激活计数是一个错误,当这种情况发生时,应用程序将生成E_OS_LIMIT错误(即使在标准构建状态下)。

任务可以从两个任务和(第2类)ISRs中激活。

激活任务并不会使任务立即开始执行,它只是使任务准备好运行。然而,RTA-OS需要检查激活的任务是否比当前运行的任务具有更高的优先级,如果是,则导致上下文切换,以便新任务可以抢占当前运行的任务。

当您从另一个任务激活一个任务RTA-OS时,确切的行为取决于相对的任务优先级。如果激活的任务优先级高于当前运行的任务,则新激活的任务将抢占当前任务。否则,该任务将保持在就绪队列中,直到它成为最高优先级的就绪任务。在一个设计良好的实时系统中,一个任务激活一个更高优先级的任务是不寻常的。通常isr捕获系统触发器,然后激活任务以执行任何相关处理。反过来,这些任务可能激活较低优先级的任务,以实现具有较长截止日期的触发器响应。

观察这一事实导致了RTA-OS中的一个主要优化。如果指定任务从不激活高优先级任务,RTA-OS可以消除测试每次激活后是否需要上下文切换的内部代码。这是通过选择“禁止向上激活”优化来配置的。

这类似于从ISR激活任务时的行为。所有ISR都有一个严格高于最高任务优先级的优先级。

当一个任务从ISR激活时,它永远不会立即进入运行状态,因此不需要检查上下文切换。

只有在离开ISR时才需要这样的检查。

2.8.1 直接激活Direct Activation

任务可以通过许多不同的方式激活。任务激活的基本机制是ActivateTask() API调用,它直接激活任务。ActivateTask(TaskID)调用将指定任务置于就绪状态。ChainTask(TaskID)调用终止了调用任务(见2.11节),并将命名任务置于就绪状态。

2.8.2 间接激活Indirect Activation

除了直接激活任务外,还可以使用其他AUTOSAR OS机制间接激活任务。这些方法在后面的章节中有更详细的描述。

报警激活(Activation by an Alarm)。对于系统中的每个告警,您可以指定每次告警过期时激活的任务。

Example 2.5: Using Direct Activation Chains

通过调度表激活(Activation by a Schedule Table)。对于系统中的每个调度表,可以指定在表上的一个或多个到期点上激活的任务。

2.9 控制任务执行顺序Controlling Task Execution Ordering

在许多情况下,您需要限制特定任务的执行顺序。在基于数据流的设计中尤其如此,其中一个任务需要在另一个任务使用计算值之前执行一些计算。如果执行顺序不受约束,则可能出现竞态条件,应用程序行为将不可预知。可以通过以下方式控制任务的执行顺序:

•直接激活链(见2.9.1节)。

•优先级(见章节2.9.2)。

•非抢占任务

2.9.1 直接激活链Direct Activation Chains

当使用直接激活链来控制执行顺序时,任务对必须在进行调用的任务之后执行的任务执行ActivateTask()调用。

有三个任务Task1、Task2和Task3,它们必须按照Task1、Task2、Task3的顺序执行。

示例2.5给出了任务体示例。

图2.16显示了假设Task1优先级最高,Task 3优先级最低,这些任务将如何执行。

图2.16 控制任务执行顺序的直接激活

2.9.2 使用优先级Using Priority Levels

约束任务执行顺序的优先级级方法可以用来利用抢占调度策略的性质来控制激活顺序。

回顾第2.1节,在固定优先级抢占调度下,调度器总是运行最高优先级的任务。如果许多任务被释放到就绪队列中,它们将按优先级顺序执行。这意味着您可以使用任务优先级来控制执行顺序。

从前面的例子来看,在例2.5中,让我们假设Task1的优先级最高,Task3的优先级最低。这意味着可以重写任务主体以利用优先级控制的激活。这可以在例2.6中看到。

图2.17显示了如何执行这些任务。

Example 2.6: Using Priority Level Controlled Activation

图2.17 使用优先级来控制任务的执行顺序

2.10 RTA-OS中的合作调度Co-operative Scheduling in RTA-OS

当一个任务是非抢占式运行时,它会阻止任何任务(包括那些高优先级的任务)的执行。然而,有时对于非抢占式任务来说,提供可以进行重调度的显式位置是有用的。这比简单地非抢先运行更有效,因为高优先级任务对系统刺激的响应时间更短。在一个系统中,任务以非抢先的方式运行,并为重新调度提供点,这种系统被称为协作调度系统。

Schedule() API调用可用于暂时消除非抢占任务和使用内部资源的任务施加的抢占约束。

当调用Schedule()时,允许运行任何优先级高于调用任务的就绪任务。Schedule()直到所有高优先级任务结束才返回。

例2.7展示了一个非抢占任务Cooperative,它包括一系列函数调用。一旦启动,每个函数运行到完成时不会抢占,但是任务本身可以在每个函数调用之间被抢占。

图2.18显示了Task1和Task2这两个相互协作的任务在战时如何发挥作用。白色部分表示不可抢占的代码部分。

Example 2.7: Making a task run co-operatively

图2.18 合作任务Co-operative tasks

2.10.1 优化Schedule() API Optimizing out the Schedule() API

Schedule()在完全抢占式系统中没有用处。如果不打算使用它,可以使用“优化,RTA-OS, disallow Schedule()”来禁止在rtaoscfg中调用Schedule()。如果不允许对Schedule()的调用,那么将看到系统的最差情况堆栈需求降低了。

2.11 终止任务Terminating Tasks

在AUTOSAR操作系统中终止的任务必须通过API调用来告诉操作系统正在发生这种情况。AUTOSAR OS标准为任务终止定义了两个API调用。必须使用其中一个来终止任何任务。这些API调用是:

•TerminateTask ()

•ChainTask (TaskID)

当一个任务完成时,它必须调用这些API中的一个。这确保RTA-OS可以正确地调度下一个准备运行的任务。

TerminateTask()强制调用任务进入挂起状态。然后RTA-OS将在就绪状态下运行下一个优先级最高的任务。

ChainTask(TaskID)终止调用任务,激活任务TaskID。因此,该API就像执行一个TerminateTask(),然后立即执行ActivateTask(TaskID)。链接任务将指定的任务置于就绪状态。

2.11.1 优化RTA-OS中的任务终止Optimizing Termination in RTA-OS

AUTOSAR OS标准允许任务在任何时候调用任务终止API调用,包括在嵌套很深的函数调用集中。

这是一种糟糕的编程实践——相当于goto的使用。

在运行时,RTA-OS必须存储允许它在任务终止时清除堆栈的信息,而不是入口函数。这通常使用setjmp/longjmp对来完成。

例2.8显示了对其他函数进行嵌套调用的任务。Task1运行时,它调用Function1()。

Function1()然后调用Function2()。Function2()包含可以终止调用任务的代码(在本例中是Task1)。

然而,单堆栈架构的一个关键好处是,在其入口函数中终止的任务可以简单地返回- TerminateTask()不需要做任何事情。如果所有的任务都没有终止,或者只是在它们的入口函数中终止,那么RTA-OS保存的允许从任何地方返回的上下文都不需要存储。

RTA-OS允许您使用快速终止优化(Optimizations➔fast Terminate)来开发良好的应用程序设计。当所有执行TerminateTask()或ChainTask() api的任务只在它们的entry函数中执行此优化时,您可以启用此优化。优化告诉RTA-OS不生成代码以节省不必要的上下文,从而节省堆栈空间。

Example 2.8: Terminating a Task

2.12 延迟任务Delayed Tasks

OS选项“支持延迟任务执行”可用于添加对api Os_SetDelayedTasks()、Os_AddDelayedTasks()和Os_RemoveDelayedTasks()的支持。

这些api允许您告诉RTA-OS延迟一组任务的执行。延迟任务可以被激活,但直到从集合中删除它们才会实际运行。

Os_SetDelayedTasks()用于指定需要延迟哪些任务。如果一个任务在调用之前被延迟,但它不在新的延迟任务集中,那么如果它的优先级高于调用方,那么它将在此调用返回之前执行。

您必须只设置在调用核心上运行的任务。

Os_AddDelayedTasks()用于向已有的延迟任务集中添加任务。多次添加任务是允许的,但没有效果。必须只添加在调用核心上运行的任务。

Os_RemoveDelayedTasks()用于从去袒护的任务集中移除任务。如果被删除的任务的优先级高于调用方,则它们将在此调用返回之前执行。

注意,如果某个特定核心上的任务共享优先级,则必须指定共享优先级的所有任务或不指定优先级。当启用延迟任务时,任务状态模型变得有点复杂。下表试图解释可能的转换。

2.13 空闲机制The Idle Mechanism

当没有任务或ISR要运行时,任何抢占式操作系统都必须有事可做。在AUTOSAR OS中,这是通过空闲机制实现的。在RTA-OS中,当没有任务或ISR要运行时,操作系统将处于繁忙等待循环中,什么也不做。

但是,可以通过声明一个名为Os_Cbk_Idle的回调来提供您自己的空闲机制实现,从而覆盖默认行为。

Os_Cbk_Idle的行为与任务相同,除了:

•无法激活

•不能终止

•它不能等待事件

•它不能被束缚

•不能使用内部资源

Os_Cbk_Idle的优先级比系统中的任何任务都低,因此它只在没有准备运行的任务(或ISR)时运行。

因此,空闲机制为您提供了一个几乎完全不受系统开销影响的“额外任务”。

例2.9显示了Os_Cbk_Idle用于控制RTA的实现。

Os_Cbk_Idle在退出时返回一个布尔值,告诉RTA-OS是否再次调用Os_Cbk_Idle。当返回TRUE时,RTA-OS立即再次调用Os_Cbk_Idle。当返回FALSE时,RTA-OS停止调用Os_Cbk_Idle,并进入繁忙等待循环的默认行为。

2.14 任务开始和结束的构子函数Pre and Post Task Hooks

假设需要在每个任务开始之前和/或在每个任务结束之后执行一些代码,例如分析执行的跟踪。可以使用AUTOSAR OS提供的PreTask和PostTask钩子来实现这一点。

当任务进入运行状态时,RTA-OS会调用PreTask钩子。

这意味着当一个任务在抢占后恢复时,PreTask钩子也将被调用。

当任务移出运行状态时,RTA-OS会调用PostTask钩子。

PostTask钩子将在任务终止时调用,并且每次任务被抢占时调用。

图2.19显示了相对于任务抢占,PreTask和PostTask钩子被调用的位置。

这两个钩子只有在配置时才被调用。图2.20显示了如何启用钩子。

图2.19 PreTaskHook()和PostTaskHook()相对于任务抢占

图2.20 Enabling the PreTaskHook() and PostTaskHook()

Example 4.10: The PreTaskHook and PostTaskHook

例2.10展示了钩子应该如何出现在代码中。

在任务进入和退出时以及每次抢占/恢复时调用PreTask和PostTask钩子。这意味着可以使用这些钩子记录应用程序的执行跟踪。由于应用程序中的所有任务都必须使用相同的PreTask和PostTask钩子,因此有必要使用GetTaskID() API调用来确定在进入钩子例程时哪个任务已经或将要运行。

RTA-OS定义了一组宏,这些宏仅在对应的钩子被启用时才被定义。这些宏被称为:

•OS_PRETASKHOOK

•OS_POSTTASKHOOK

Example 2.11: Conditional Compilation of PreTaskHook

这允许编写代码,其中可以有条件地编译钩子,如示例2.11所示。

2.15 通过抢占保存硬件寄存器Saving Hardware Registers across Preemption

RTA-OS在上下文切换时尽可能少地保存上下文—只保存操作系统正确操作的上下文。但是,可能会发现需要在运行时保存和恢复附加的依赖于应用程序的上下文。例如,可能有使用浮点寄存器的任务,因此需要通过上下文切换保存微控制器的浮点上下文。

可以选择使用PreTask和PostTask钩子和应用程序管理的堆栈手动实现这一点。但是,很难在不改变操作系统配置的情况下优化这种类型的实现。可以:

•始终保存每个交换机上的上下文到一个任务,然后在每个交换机上恢复。这个模型意味着你可能会做不必要的保存和恢复(例如,当切换到一个不使用它的任务时,保存一个寄存器集);或

•离线计算所需的保存,然后编写一个更复杂的钩子对,使用GetTaskID()/GetISRID()来计算是否需要保存/恢复。这个模型是脆弱的,因为对配置的更改,例如添加新的任务/ isr或修改优先级,将意味着需要重新工作。为了避免这些问题,RTA-OS提供了一种简单的通用机制,用于保存特定于用户的上下文和操作系统上下文。RTA-OS能够利用其优先级空间的knowl edge来精确计算哪些任务需要在运行时保存寄存器集,从而优化掉不必要的保存,节省上下文切换所需的时间和堆栈。例如:

•如果你只有一个任务或二类ISR使用一个给定的寄存器集,那么不需要保存或恢复。

•如果多个任务使用相同的寄存器集,但不能同时执行(因为它们不可抢占,共享内部资源或共享优先级),那么RTA-OS不需要保存寄存器集。

图2.21 Register saving in action

•上下文切换到使用寄存器集的最低优先级任务不需要进行保存,因为可以保证没有其他任务可以使用该集(因为如果高优先级任务正在使用寄存器集,则最低优先级任务不可能运行)。

•类似地,从使用寄存器集的最高优先级任务进行上下文切换不需要进行保存,因为没有更高优先级的任务使用寄存器集,因此不会破坏上下文。

图2.21显示了由任务1、3和5共享的寄存器集。可以看到,当不需要保存时(当切换到不使用寄存器集的任务时),就不会进行上下文保存。

需要保存的每个寄存器集都需要在配置时声明给RTA-OS。Rtaosgen使用声明定义两个回调函数,必须提供它们来保存和恢复寄存器集。图2.22显示了三个寄存器集的定义。

使用寄存器集的每个任务都需要在运行时声明这一点,以便rtaosgen可以计算需要保存的最大集数。图2.23显示了如何为任务执行此操作。

RTA-OS不知道如何或在哪里保存和恢复寄存器集——它只知道需要保存多少次以及何时保存和重新存储它们。对于定义的每个寄存器集,RTA-OS生成一个宏OS_REGSET__SIZE,用于定义所需的最坏情况下的寄存器集保存数量。应该在应用程序代码中使用它来定义一个大小为OS_REGSET__SIZE的数组,其中数组的每个元素都包含保存的寄存器集。

图2.22 寄存器集定义

图23. 在一个任务中使用寄存器集

Example 2.12: Register Set Save And Restore

你还需要为保存和恢复操作提供回调函数:

•Os_Cbk_RegSetSave_(Os_RegSetDepthType Depth)在需要保存寄存器集时被RTA-OS调用。

•Os_Cbk_RegSetRestore_(Os_RegSetDepthType Depth)在需要恢复寄存器集时被RTA OS调用。

两个回调函数都传递了一个Depth值,该值指示要保存或恢复的寄存器集。

例2.12显示了回调函数应该如何出现在你的代码中。

2.16 小结

•任务是一个并发活动。

•有两类任务:基本任务和扩展任务。

•任务可以共享优先级,但建议不要这样做。

•根据优先级安排任务。

•当一个高优先级的任务准备好运行时,它将抢占低优先级的任务,但它不会抢占任何已配置为非抢占的任务。

•任务以就绪、运行、挂起或等待状态存在(但是,只有已扩展的任务可以进入等待状态)。

•如果一个任务终止,它必须调用TerminateTask()或ChainTask(TaskID)来终止。

•所有任务在其入口函数中终止的系统可以使用“快速终止”优化来最小化堆栈使用和上下文切换时间。

•任务只能在处于挂起状态时被激活,除非指定了多个激活。•PreTask和PostTask钩子允许你在任务开始前和结束后执行代码。这可用于在运行时分析应用程序。

参考文档:

[1] RTA-OS V6.1.3User Guide

上一篇 : 写景古诗手抄报_写景古诗

下一篇 : 最后一页

x

相关推荐