电子说
1.概述
在《RTA-OS系列介绍-Task》部分我们介绍了任务分为基础任务与扩展任务,两者的主要区别为,扩展任务多了waiting状态,那Waiting状态等待的是什么呢?其实就是我们今天要介绍的Events(事件),当系统中的Task或ISR设置事件后,等待的任务将转到Ready状态。当它成为最高优先级就绪任务时,RTA-OS将选择运行该Task。
在AUTOSAR操作系统中,事件用于向任务发送信号信息,主要用于为扩展任务提供多个同步点。本文将对什么是事件,如何配置事件以及如何在运行时使用它们。Events的使用场景大致如下图所示。
2. Events配置
正常在应用中可配置的Events的最大数量取决于硬件,而Events需要配置的内容包括:名字、至少一个Task使用及Event mask。
设置事件时,必须同时指定任务。因此,例如,如果为名为Task1的任务设置名为Event0的事件,则这对任务Task2的Event0没有影响。
2.1 定义等待任务
在使用中,当我们声明某个Task需要等待一个Event时,系统将默认该任务为扩展任务,等待事件的扩展任务通常会自动启动(等待的时间满足后),并且任务永远不会终止。当任务开始执行时,RTA-OS将清除它拥有的所有事件。
3. 如何使用Event
3.1 等待事件
任务的等待事件需要调用WaitEvent(EventMask) API,具体等待的EventMask需要关联到提前声明的内容。
WaitEvent()将事件作为其唯一参数。执行调用时,有两种可能:
1)事件暂未发生。这种情况下该Task会进入等待状态,RTA-OS会运行Ready状态中优先级最高的Task。
2)事件已经发生。在这种情况下,任务将保持在运行状态,并将在WaitEvent()调用之后的语句中继续执行。
3.1.1 等待单一事件
要等待单个事件,只需将事件掩码名称传递给API调用。下面示例显示了任务如何使用等待事件。
#includeTASK(ExtendedTask) { ... WaitEvent(Event1); /* Task enters waiting state in API call if Event1 has not happened */ /* When Event1 is set, ExtendedTask resumes here */ ... }
在AUTOSAR操作系统中,为处于挂起状态的任务设置事件是非法的。实际上,这意味着等待事件的任务结构通常是一个等待事件的有限循。
3.1.2 等待多个事件
因为AUTOSAR OS事件只是一个位掩码(Bit Mask),所以用户可以通过按位设置一组位掩码,同时等待多个事件。
当任务等待多个事件时,当等待的任何一个事件发生时,它将恢复。当从等待多个事件恢复时,将需要确定发生了哪些事件。
#includeTASK(ExtendedTask){ EventMaskType WhatHappened; while(true){ WaitEvent(Event1|Event2|Event3); GetEvent(Task1, &WhatHappened); if( WhatHappened & Event1 ) { /* Take action on Event1 */ ... } else if( WhatHappened & Event2 ) { /* Take action on Event2 */ ... } else if( WhatHappened & Event3 ) { /* Take action on Event3 */ ... } } }
在AUTOSAR-OS中,提供了GetEvent()的API,我们可以通过该API获知哪个事件已完成。
3.1.3 扩展任务的死锁
虽然AUTOSAR操作系统在关键部分的资源互斥中提供了免于死锁的自由,但在构建具有可能死锁的事件的系统时,仍不会受到保护。如果我们有相互设置和等待事件集的扩展任务,则两个(或更多)任务可能正在等待仅由其他正在等待的任务设置的事件。当然,即使存在死锁扩展任务,系统中的基本任务也不可能死锁。
下面的样例展示了扩展任务的死锁:
#includeTASK(Task1) { while (1) { WaitEvent(Ev1); /* Never reach here - DEADLOCKED with Task2! */ SetEvent(Task2,Ev2); } } TASK(Task2) { while (1) { WaitEvent(Ev2); /* Never reach here - DEADLOCKED with Task1! */ SetEvent(Task1,Ev1); } }
OS配置不获取哪些任务或ISR设置了事件,只获取哪些任务可以等待事件。因此,RTA-OS不可能静态地确定扩展任务是否会死锁。采用下面的设计方法可能会避免类似问题:
•仅使用基本任务;
•分析代码,以表明在所有SetEvent()或WaitEvent()对的传递闭包上没有循环等待事件。
3.2 设置事件
通过SetEvent() API 来设置事件。
SetEvent()调用有两个参数,一个任务和一个事件掩码。对于指定的任务,SetEvent()调用设置事件掩码中指定的事件。该调用不会为共享事件的任何其他任务设置事件。
在调用SetEvent()时,可以按位或多个事件掩码来同时为任务设置多个事件。
无法为处于挂起状态的任务设置事件。因此,在设置事件之前,必须确保任务未挂起。您可以使用GetTaskState()API调用来实现这一点,但请注意,当为优先级高于调用方的任务调用此函数时,可能存在竞争条件。调用方可以在对API的调用和对结果的评估之间被抢占,并且被请求的任务的状态在中间时间内可能已经改变。
当扩展任务正在等待的任何一个事件被设置时,扩展任务将从等待状态移动到就绪状态。
如下任务显示了任务如何设置事件:
#includeTASK(Task1) { TaskStateType TaskState; /* Set a single event */ SetEvent(Task2, Event1); /* Set multiple events */ SetEvent(Task3, Event1 | Event2 | Event3); ... /* Checking for the suspended state */ GetTaskState(Task2,&TaskState); if (TaskState != SUSPENDED) { SetEvent(Task2, Event1); } ... TerminateTask(); }
多个任务可以同时等待同一个事件,然而从上面例子可以看出,事件没有广播机制,换句话说,不能通过调用一个API告诉所有等待的任务该事件已经发生。
此外,也可以通过Alarms及调度表来设置事件。
3.2.1通过Alarm设置事件
Alarm可用于定期激活不终止的扩展任务。每次Alarm到期时,都会设置该事件。等待事件的任务随后准备好运行。
3.2.2 通过带有到期点的调度表设置事件
调度表上的到期点可用于编程(a)非终止状态的扩展任务的定期激活。每次处理到期点时,都会设置事件。等待事件的任务随后准备好运行。
3.3 清除Events
可以通过Task或者ISRs来设置Event,但是Event只能被其owner清除。
#includeTASK(ExtendedTask){ EventMaskType WhatHappened; ... while( WaitEvent(Event1|Event2|Event3)==E_OK ) { GetEvent(Task1, & WhatHappened); if(WhatHappened & Event1 ) { ClearEvent(Event1); /* Take action on Event1 */ ... } else if( WhatHappened & (Event2 | Event3 ) { ClearEvent(Event2 | Event3); /* Take action on Event2 or Event3*/ ... } } }
当某个任务等待某个事件,该事件发生,在后面时序再次对同一个事件调用WaitEvent()时,由于该事件仍处于Set状态,会立即返回。因此,在再次调用等待事件前需要将之前已发生事件清除。
清除事件时调用ClearEvent API,被清除后的状态必须与事件掩码关联起来。
当某个任务被挂起时,其所拥有的Event将被自动清除。
3.4 用基础任务模拟扩展任务
基础任务只能在任务执行的开始或结束时同步。
如还有其他同步节点需要时,可以通过event机制来实现。然而,扩展任务较基础任务占用资源更多,在资源限制的系统中,只能通过使用基础任务来进行 同步。
例如,如果任务构建为状态机(例如,使用C switch语句),则可以设置状态变量,发出TerminateTask()调用并等待重新激活。如下样例代码显示了如何实现这一点。
#include/* Create a "State" variable that remains in scope between task activations */ uint8 State; TASK(Task1) { switch (State) { case 0: /* Synchronization point 0. */ State = 1; break; case 1: /* Synchronization point 1. */ State = 2; break; case 2: /* Synchronization point 2. */ State = 0; break; } TerminateTask(); }
4.本文总结
Event是用于同步的实体,可用于扩展任务的等待内容;
同一个Event可被不同的Task引用;
Event不具有广播机制,即无法将信息通知所有等待该Event中的任务;
Tasks,ISRs及调度表都可以设置Events。
如果时效性在系统中很重要,则所有扩展任务(任何等待事件的任务)的优先级必须低于基本任务。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !