符合AUTOSAR标准的RTA-OS-Resources详解

电子说

1.2w人已加入

描述

正文

4.资源Resources

访问需要在任务和ISR之间共享的硬件或数据可能是不可靠和不安全的。这是因为当较低优先级的任务或ISR正在更新共享数据时,可能会发生任务或ISR抢占。这种情况被称为竞态条件,非常难以测试。

访问共享数据的语句序列称为临界区。为了提供对临界区中引用的代码和数据的安全访问,需要强制互斥。换句话说,必须确保在临界段期间,系统中没有其他任务或二类中断能够抢占正在执行的任务

在Task章节中,看到可以将任务声明为非抢占式可以防止出现互斥问题。然而,这种方法是治标不治本的,因为它通过防止出现抢占来防止抢占带来的问题。

操作系统提供基于资源的互斥机制。资源Resources只是一个二进制信号量。当一个任务或二类ISR获得资源时,其他任务或ISR无法获得该资源。这可以防止任何其他任务或ISR同时进入相同的临界区。当临界区结束时,任务或ISR释放资源,另一个任务/ISR可以进入临界区。

当高优先级任务被低优先级任务阻止执行时,这称为优先级反转,因为高优先级任务比低优先级任务需要更长的时间来完成它的执行。较低优先级的任务似乎优先于较高优先级的任务运行,这与实际优先级分配的预期相反。高优先级的任务被低优先级的任务阻塞。

由于优先级反转会在系统中引入无界阻塞,二进制信号量在传统操作系统中经常有不好的名声。例如,如果低优先级任务阻止高优先级任务执行,但它本身被一个不需要访问共享资源的中等优先级任务抢占,那么高优先级任务也将被中等优先级任务的执行阻塞。由于低优先级任务在持有共享资源时可能会被抢占多次,因此高优先级任务遭受的阻塞可能是无限的,如果需要确定任务响应所需的最长时间,则会带来严重的问题。在极端情况下,任务可能会达到一种被称为“死锁”的状态,在这种状态下,每个任务都在等待进入一个临界区,而这个临界区正在被其他任务使用。

在AUTOSAR OS中,避免了通常与优先级反转和死锁相关的问题,因为资源是根据锁定协议锁定的。这个锁协议称为优先级上限协议(优先级天花板协议),特别是称为立即继承优先级上限协议(或堆栈资源协议)的版本。

优先级上限协议使用了优先级上限的概念。系统中的每个资源都被分配了一个上限优先级,该优先级等于需要访问该资源的任何任务或ISR的最高优先级。当任务或ISR获得资源时,任务/ISR的运行优先级将增加到资源的最高优先级(当且仅当该优先级高于任务/ISR当前的运行优先级)。当资源被释放时,任务或ISR的优先级恢复到该任务或ISR发出呼叫之前的优先级。如图4.1所示。

WIPL

图4.1 提升至最高优先级

立即继承优先级上限协议提供了两个主要优点:

1)优先级反转被最小化

每次高优先级任务或ISR准备就绪时,它的执行最多只能被一个已经持有资源的低优先级任务或ISR延迟一次。这意味着没有累积阻塞,因此可以对任务遭受的阻塞设置上限-最大阻塞时间是低优先级任务/ISR持有共享资源的最长时间。此外,这种阻塞总是在执行开始时发生。这样做的结果是,资源在需要锁定时总是处于空闲状态。在AUTOSAR操作系统中不需要等待资源被释放。

2)它保证没有死锁

一个任务或ISR必须在执行中才能产生锁。假设一个任务(或ISR)试图获取资源。如果另一个任务或ISR已经拥有资源,那么由于该任务或ISR必须以最高优先级运行,因此发出请求的任务不会执行(它不会是系统中最高优先级的任务或ISR),因此不能尝试锁定资源。

4.1 配置资源Resource Configuration

在最基本的级别上,资源只需要命名和分配类型。AUTOSAR OS中有三种类型的资源:

1)标准资源Standard Resources是普通的操作系统信号量。配置标准资源将创建具有指定名称的资源。

2)链接资源Linked Resources允许为标准(或另一个链接的)资源设置别名,以便可以对同一资源进行嵌套锁定。这些将在第4.4节中详细讨论。

2)内部资源Internal Resources是在进入任务时自动锁定并在任务结束时自动释放的资源。这些将在第4.5节中详细讨论。

WIPL

图4.2:在rtaoscfg上配置资源

RTA-OS需要知道哪些任务和ISRs使用了哪些资源。然后,它可以计算优先级上限协议所使用的上限优先级。

 

#include 
TASK(Task1) {
  ...
  GetResource(Resource1);
  /* Critical section. */
  ReleaseResource(Resource1);
...
  TerminateTask();
}

 

Example 4.1: Using Resources

可以在任务或ISR配置期间配置每个任务或ISR的其他资源使用信息。

图4.2显示了已经声明了一个名为Resource1的资源。在程序中引用此资源时,必须使用相同的名称。

4.2 中断级别的资源Resources on Interrupt Level

在AUTOSAR OS中,任务和中断之间共享的资源是可选的。RTA-OS支持这个可选特性。

RTA-OS将自动识别组合资源,因此您不需要进行任何特殊配置。

当任务获得与ISR共享的资源时,RTA-OS将屏蔽所有中断,中断优先级小于或等于共享资源的最高优先级中断。

这只是优先级上限协议的扩展。在任务和ISR之间共享资源意味着提供了比启用/禁用和暂停/恢复API调用更大的对内部中断屏蔽的控制,因为它们可以将中断子集屏蔽到特定的优先级级别。因此,当使用支持嵌套中断的RTA-OS端口时,中断级别的资源特别有用。

4.3 使用资源Using Resources

可以使用GetResource() API调用获取资源。然后,您可以使用ReleaseResource()调用释放资源。一个任务或ISR必须在释放了它所锁定的所有资源之后才能终止。

任务或ISR只能使用在RTA-OS配置过程中指定的资源。例4.1展示了如何在Task1中使用资源。

对GetResource()和ReleaseResource()的调用必须匹配。无法获取已锁定的资源。无法释放尚未锁定的资源。

WIPL

图4.3:执行具有资源锁的任务

当执行GetResource()时,它将调用任务或ISR的优先级提升到资源的最高优先级。资源的上限优先级是共享该资源的所有任务或ISR的最高优先级,由RTA-OS自动计算。如果任何优先级低于上限优先级的任务准备好运行,那么它将被阻止执行(它将被阻塞),直到正在运行的任务的优先级恢复正常。

图4.3通过以下配置显示了此效果:

WIPL

Task 2的第一次激活被阻塞,因为Task 1已经锁定了R2。Task 2的第二次激活也被阻塞,但这次是因为Task 1锁定了R1。Task 3的第一次激活同样被阻塞,因为Task 1持有R1。当Task 1释放R1时,操作系统运行优先级最高的就绪任务Task 3。在Task 3终止时,执行Task 2,最后,当Task 2终止时,继续执行Task 1。

4.3.1 嵌套资源调用Nesting Resource Calls

可以同时获得多个资源,但是API调用必须严格嵌套。我们来看两个例子;一个显示嵌套错误的调用,另一个显示嵌套正确的API调用。

例4.2显示Resource1和Resource2以错误的顺序被释放。

例4.3展示了一个正确嵌套的示例。所有的资源都被保留,然后按正确的顺序释放。

 

GetResource(Resource1);
  GetResource(Resource2);
ReleaseResource(Resource1); /* Illegal! */
/* You must release Resource2 before Resource1 */
  ReleaseResource(Resource2);

 

Example 4.2: Illegal Nesting of Resource Calls

 

GetResource(Resource1);
  GetResource(Resource2);
    GetResource(Resource3);
    ReleaseResource(Resource3);
  ReleaseResource(Resource2);
ReleaseResource(Resource1);

 

Example 4.3: Correctly Nested Resource Calls

4.4 链接资源Linked Resources

在AUTOSAR操作系统中,对相同资源的GetResource() API调用不能嵌套。但是,有时可能需要嵌套资源锁。

应用程序可能使用在多个任务之间共享的函数。如果共享函数需要获得一个任务使用的资源,而其他任务不需要,会发生什么情况?看一下例4.4。

 

#include 
void SomeFunction(void) {
  GetResource(Resource1); /* !!! Not allowed if caller is Task1 !!! */
  ...
  ReleaseResource(Resource1); /* !!! Not allowed if caller is Task1 !!! */
}




TASK(Task1) {
  GetResource(Resource1);
  /* Critical section. */
  SomeFunction();
  ReleaseResource(Resource1);
}




TASK(Task2) {
  SomeFunction();
}

 

Example 4.4: Illegal locking of previously locked resource

在这些情况下,(可能)持有的资源的嵌套必须使用链接的资源。链接资源是现有资源的别名,并保护相同的共享对象。

图4.4显示了如何使用rtaoscfg声明链接的资源。

WIPL

图4.4:配置链接资源

使用链接的资源,例4.4将被重写为例4.5所示。

 

#include 
void SomeFunction(void) {
  GetResource(LinkedToResource1); /* Okay */
  ...
  ReleaseResource(LinkedToResource1); /* Okay */
}


TASK(Task1) {
  GetResource(Resource1);
/* Critical section. */
  SomeFunction();
  ReleaseResource(Resource1);
}
TASK(Task2) {
  SomeFunction();
}

 

Example 4.5: Using Linked Resources

链接的资源使用与标准资源相同的API调用来保持和释放(这些将在4.3节中解释)。

也可以创建链接资源到已有的链接资源。

4.5 内部资源Internal Resources

如果一组任务非常紧密地共享数据,那么就运行时成本而言,使用标准资源来保护对每个数据项的每次访问可能过于昂贵。甚至可能无法确定需要保存资源的所有位置。

可以通过使用内部资源来防止对共享数据的并发访问。内部资源是为任务的生命周期分配的资源。使用rtaoscfg离线配置内部资源。然而,与普通资源不同的是,无法获取和释放它们。

从概念上讲,RTA-OS在启动任务之前立即锁定内部资源,并在任务结束后立即释放资源。

当任务进入运行状态时,RTA-OS中内部资源的实现不会产生运行时成本,因为rtaosgen会计算任务脱机运行的优先级,并按此优先级简单地调度任务。共享内部资源的任务集在配置时使用rtaoscfg静态定义。

注意:在AUTOSAR OS中,内部资源仅对任务可用。但是,也没有理由不能由第一类和第二类ISR共享内部资源。RTA-OS提供了AUTOSAR OS的扩展,允许ISR使用内部资源。当任务锁定与ISR共享的内部资源时,则任务在中断的IPL处执行,并且在任务期间阻塞所有同等或更低优先级的中断。

图4.5显示了一个内部资源(称为InternalResource)的声明,它在任务t1和任务t3之间共享。

WIPL

图4.5 使用rtaoscfg声明内部资源

如果任务使用内部资源,RTA-OS将在调用任务的入口函数之前自动获取内部资源。然后,在任务终止、调用Schedule()或WaitEvent()之后,资源将自动释放。

在任务执行过程中,所有其他共享内部资源的任务将被阻止运行,直到内部资源释放。

图4.6显示了共享相同内部资源的三个任务的执行情况。

WIPL

图4.6 内部资源的执行

重要的是要注意,当一个拥有内部资源的任务完成时,操作系统会根据就绪任务的正常(基本)优先级做出调度决策。如果一个任务正在运行,并且共享相同内部资源的多个任务已经激活,那么在运行的任务终止时,将选择优先级最高的就绪任务来运行,然后按内部资源的最高优先级调度。

任何优先级低于内部资源优先级上限的任务(包括不共享内部资源的任务)在执行共享内部资源的任务时将被阻塞。可以在图4.6中看到一个示例,其中优先级为1的Task1与优先级为3的Task共享内部资源。如果Task1在Task2或Task3启动之前开始运行,那么这两个任务将被延迟(阻塞),直到Task1完成。

然而,所有不共享内部资源的高优先级任务仍然可以抢占。图4.7显示,Task 1最初以优先级3运行,因为它与优先级3的任务共享一个内部资源。当Task 1运行时,Task 2就可以运行了。task2的优先级低于Task1的活动优先级,因此它不能抢占。当Task4被激活时,它可以抢占Task1,因为它的优先级是4,即它的优先级高于Task1的活动优先级。任务2只能在任务1终止时运行。

WIPL

图4.7 内部资源阻塞了不共享资源的任务

从这种行为可以清楚地看出,锁定内部资源的任务将阻止任何优先级高于自身但优先级低于内部资源最高优先级的任务在整个任务期间运行。当低优先级任务阻止高优先级任务执行时,称为阻塞。共享内部资源的任务相对于彼此非抢占性地运行。

一旦共享内部资源的集合中的任务获得了对CPU的访问权,它将在不被集合中的任何其他任务抢占的情况下运行。这样做的结果是,与完全抢占式系统相比,高优先级任务可能需要更长的时间才能访问CPU。

4.6 使用资源以最小化堆栈使用Using Resources to Minimize Stack Usage

资源在应用程序中的主要作用是在关键部分上提供互斥。然而,RTA-OS的单堆栈模型意味着资源有一个有用的次要角色——最小化堆栈使用。回想一下,共享资源的任务不会相互抢占。在RTA-OS使用的单堆栈模型中,这意味着它们的堆栈使用是有效覆盖的。

可以利用这个特性来权衡系统中的时间和堆栈使用情况。下面几节介绍对应用程序的简单修改如何减少堆栈使用。所有这些修改都会在系统中引入额外的阻塞因素。

这些阻碍因素的影响取决于系统。回想一下,优先级上限协议确保任务或ISR在执行期间最多被阻塞一次。最坏阻塞时间是任何低优先级任务或ISR可以占用相同资源的最大时间。

这意味着,如果额外的阻塞因素小于或等于当前任务/ISR所遭受的最坏情况阻塞,则不会对响应时间产生影响,并且减少的堆栈使用将被释放。如果额外的阻塞因素比当前的最坏情况阻塞更长,那么响应时间将更长。如果响应时间保持在任务/ ISR所需的截止日期之内,系统仍将正常运行。

4.6.1 内部资源Internal Resources

给定一组共享内部资源的任务,RTA操作系统使用的最坏情况堆栈等于使用最多堆栈的任务所需的最大堆栈空间。在传统操作系统中,最大堆栈空间等于任务堆栈的总和,而不是它们的最大值。

如果需要最小化堆栈空间,那么可以通过在消耗大量堆栈的任务之间共享内部资源来利用RTA-OS的单一堆栈架构的优势。图4.8中的第一个堆栈显示了5个抢占任务A、B、C、D和E的最坏情况堆栈消耗情况。通过在任务B和C之间以及任务D和E之间共享内部资源,可以显著节省堆栈空间。图4.8中的其他四个堆栈显示了现在可能发生的情况——最坏的情况是A被B的最坏情况抢占,或者C被D和e的最坏情况抢占。从图中可以看到,A被C抢占,被D抢占,这是最坏的情况,这比不使用内部资源时的堆栈要少得多。

WIPL

图4.8:使用内部资源节省堆栈空间

4.6.2 标准资源Standard Resources

如果一个任务调用一个使用大量堆栈的函数,那么可以考虑锁定函数调用周围的资源,并与优先级更高的任务共享资源。任务不需要在代码中锁定资源或调用函数——共享只是为了强制任务的执行以更高的优先级运行。这将防止高优先级任务在使用大量堆栈时抢占任务,从而减少总堆栈需求。

禁用函数调用周围的中断具有类似的效果——有效地用临时屏蔽的ISR覆盖函数调用的堆栈使用情况。

4.7 作为资源的调度程序The Scheduler as a Resource

如果一个任务有一个必须在不被系统中任何其他任务抢占的情况下执行的关键段(回想一下,调度程序是用来执行任务切换的),则该任务可以保留调度程序。为此目的,所有任务都可以使用一个名为RES_SCHEDULER的预定义资源。RES_SCHEDULER是任务共享数据的一种方便方法,无需手动声明在所有任务之间共享的资源。

当一个任务获得RES_SCHEDULER时,所有其他任务将被阻止抢占,直到该任务释放RES_SCHEDULER。这实际上意味着任务在RES_SCHEDULER被占用的时间内是非抢占性的。

这比使整个任务非抢占性要好,特别是当任务只需要在其总执行时间的一小部分防止抢占时。

必须指定应用程序是否使用RES_SCHEDULER。这是在“一般”中设置的。如果配置RES_SCHEDULER,那么RTA-OS将自动生成一个称为RES_SCHEDULER的标准资源,并在配置中的每个任务之间共享它。由于RES_SCHEDULER的行为类似于标准资源,可以创建链接到RES_SCHEDULER的链接资源,如图4.9所示。

WIPL

图4.9:Linking to RES_SCHEDULER

使用RES_SCHEDULER可以改善低优先级任务的响应时间,否则这些任务可能会受到应用程序中其他任务的多次抢占,但代价是高优先级任务的响应时间更长。

如果您不需要在应用程序中使用RES_SCHEDULER,那么您可以通过禁用它的生成来节省ROM和RAM空间,如图4.10所示。

WIPL

图4.10:Disabling RES_SCHEDULER

4.8 选择一个抢占控制机制Choosing a Preemption Control Mechanism

如果在一对GetResource()和ReleaseResource()调用之间出现不需要锁的代码,则可能会降低系统的响应性。考虑到这一点,当您在应用程序中使用资源时,应该将GetResource()调用尽可能地放置在您使用该资源保护的代码段附近。

然而,这条规则有一个例外。当有一个短时间运行的任务或ISR对同一资源进行多次GetResource()和ReleaseResource()调用时,就会出现此异常。API调用的成本可能会占整个任务执行时间的很大一部分,因此也可能占响应时间的很大一部分。

可能会发现,将整个任务或ISR主体放在GetResource()和ReleaseResource()调用之间实际上缩短了最坏情况下的响应时间。

应该尽可能避免使用非抢占式任务并获取RES_SCHEDULER。当资源被占用的时间最少,并且影响的任务数量最少时,系统的响应性和可调度性就会得到改善。

4.9 避免竞态条件Avoiding Race Conditions

调用TerminateTask()。在某些情况下,这可能会在应用程序中引入竞态约束条件。这可能会导致错过任务激活(您在本章开头了解了竞争条件)。

例4.6展示了竞态条件可能成为问题的系统类型。假设两个BCC1任务在有界缓冲区上交换数据。

在例4.6中,在资源被释放到任务结束之间,Read可以被Write抢占。当“写”任务与“读”任务连接时,激活将丢失。这是因为Read仍在运行。换句话说,一个任务正在被激活,但它没有处于挂起状态。

要解决这个问题,可以允许排队激活Read任务。这意味着您应该将任务设置为BCC2。

 

#include 
TASK(Write)
  /* Highest priority .*/
  WriteBuffer();
  GetResource(Guard);
  BufferNotEmpty = True;
  ReleaseResource(Guard);
  ChainTask(Read);
}


TASK(Read)
  /* Lowest priority. */
  ReadBuffer();
  GetResource(Guard);
  if( BufferNotEmpty ) {
    ReleaseResource(Guard);
  /* !!! Race condition occurs here !!! */
    ChainTask(Read);
  } else {
  ReleaseResource(Guard);
  /* !!! Race condition occurs here !!! */
  TerminateTask();
  }
}

 

Example 4.6: A System where a Race Condition can Occur

4.10 小结

•资源用于在访问共享数据或硬件资源时提供互斥。

•任务和ISR可以共享任意数量的资源。

•所有GetResource()和ReleaseResource()调用必须正确嵌套。

•所有资源必须在任务或ISR终止前释放。

•调度器可以作为资源使用,但如果可能的话,应该优先使用内部资源。

•内部资源为控制任务组和ISR之间的抢占提供了一种自由(Free)机制。





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分