正文
5.事件Event
在AUTOSAR OS系统中,事件用于向任务发送信号信息。本节解释事件是什么,如何配置它们以及如何在运行时使用它们。
事件可用于为扩展任务提供多个同步点。同步的可视化如图5.1所示。
扩展任务可以等待事件,这将导致任务进入等待状态。当系统中的任务或ISR设置事件时,等待任务将进入就绪状态。当它成为最高优先级的就绪任务时,RTA-OS将选择它来运行。
事件由与其关联的扩展任务拥有。通常,扩展任务将作为一个无限循环运行,其中包含对其拥有的事件的一系列受保护的等待调用。因此,事件机制允许构建事件驱动的状态机。
如果计时行为在您的系统中很重要,那么所有扩展任务(换句话说,等待事件的任何任务)的优先级必须低于基本任务。
图5.1 事件的可视化
5.1 配置事件Configuring Event
使用rtaoscfg配置事件。应用程序中可以存在的最大事件数由目标硬件决定。查阅目标/编译器端口指南,可以了解每个任务可以有多少个事件。
当声明事件时,它必须具有:
•名字。
名称仅用于在配置时指示事件的目的。
•至少有一个任务使用它。
•事件掩码。
这可以由RTA-OS自动分配。
在rtaoscfg中指定的事件名称在运行时用作事件掩码的符号名称。掩码是一个N位向量,其中N是一个任务可以等待的最大事件的最大数量。set位标识一个特定的事件。
事件名称在运行时用作掩码的符号名称。通过选择表示事件的位来配置掩码。图5.2显示了一个名为WakeUp的事件已经被声明,它将使用事件掩码中的第9位。除非确实需要分配一个特定的位位置,否则最好让RTA-OS来确定掩码值。这允许它优化bit的打包( optimize the packing of the bits),从而节省内存。
图5.2:配置事件掩码
如果一个事件由多个任务使用,则每个任务都有自己的单独副本。设置事件时,必须同时指定任务。因此,例如,如果您为一个名为t3的任务设置了一个名为Event2的事件,这对任务t4的Event2没有影响。
5.1.1 定义等待事件 Defining Waiting Tasks
使用rtaoscfg选择等待任务。如果声明一个等待事件的任务,则意味着该任务将自动被视为扩展任务。
图5.3显示了已经声明了一个事件WakeUp,并且任务TaskC和TaskD已经配置为等待该事件。
等待事件的扩展任务通常会自动启动,并且任务永远不会终止。当任务开始执行时,RTA-OS将清除它拥有的所有事件。
图5.3:选择等待事件的任务
5.2 等待事件 Waiting on Events
任务使用WaitEvent(EventMask) API调用等待事件。EventMask必须与rtaoscfg中声明的EventMask对应。
WaitEvent()将事件作为其唯一参数。当调用执行时有两种可能:
1)该事件尚未发生。在这种情况下,任务将进入等待状态,RTA-OS将在就绪状态下运行最高优先级的任务。
2)事件已发生。在这种情况下,任务保持在运行状态,并将在WaitEvent()调用之后的语句中继续执行。
5.2.1 单个事件Single Events
等待单个事件,只需将事件掩码名称传递给API调用。示例5.1显示了任务如何等待事件。
#includeTASK(ExtendedTask) { ... WaitEvent(Event1); /* Task enters waiting state in API call if Event1 has not happened */ /* When Event1 is set, ExtendedTask resumes here */ ... }
Example 5.1: Waiting on an Event
在AUTOSAR操作系统中,为处于挂起状态的任务设置事件是非法的。在实践中,这意味着等待事件的任务结构通常是一个等待事件的无限循环,如例5.2所示。
#includeTASK(ExtendedTask){ /* Entry state */ while(true){ WaitEvent(Event1); /* State 1 */ WaitEvent(Event2); /* State 2 */ WaitEvent(Event3); /* State 3 */ } /* Task never terminates */ }
Example 5.2: Simple 3-state State Machine with Events
5.2.2 多个事件Multiple Events
因为AUTOSAR OS事件只是一个位掩码,所以可以通过按位顺序排列一组位掩码来同时等待多个事件。
当任务等待多个事件时,当等待的任何一个事件发生时,它将被恢复。当从等待多个事件中恢复时,需要计算出发生了哪个(或多个)事件。
#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 */ ... } } }
Example 5.3: Waiting on Multiple Events
AUTOSAR OS提供了GetEvent() API调用,允许获取为任务设置的当前事件集。
例5.3展示了任务如何同时等待多个事件,然后在恢复时识别哪些事件已被设置。
5.2.3 扩展任务的死锁 Deadlock with Extended Tasks
虽然AUTOSAR OS在临界区互斥中提供了免于死锁的方法(参见第4章),但不能避免使用可能死锁的事件构建系统。如果有相互设置和等待事件集的扩展任务,那么可能会有两个(或更多)任务正在等待事件,而这些事件仅由正在等待的其他任务设置。当然,系统中的基本任务不可能死锁,即使存在死锁的扩展任务。
例5.4展示了两个任务,如果没有其他任务设置Ev1或Ev2,它们将死锁。
操作系统配置不捕获哪些任务/ ISR设置事件,只捕获哪些任务可以等待事件。因此,RTA-OS不可能静态地确定扩展任务是否会死锁。然而,以下设计方法可能会有所帮助:
•只使用基本任务
•分析代码,以显示在所有SetEvent()/WaitEvent()对的传递性闭包上没有循环等待事件
#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) } }
Example 5.4: Deadlock with Extended Tasks
5.3 设置事件Setting Events
使用SetEvent() API设置事件。
SetEvent()调用有两个参数,一个任务和一个事件掩码。对于指定的任务,SetEvent()调用设置事件掩码中指定的事件。该调用不为共享事件的任何其他任务设置事件。可以在调用SetEvent()时按位或多个事件掩码,以同时为任务设置多个事件
无法为处于挂起状态的任务设置事件。因此,在设置事件之前,必须确保任务没有挂起。
可以使用GetTaskState() API调用来实现这一点,但请注意,当对比调用者优先级更高的任务调用此方法时,存在潜在的竞争条件。调用者可能在调用API和评估结果之间被抢占,并且被请求的任务的状态可能在这段时间内发生了变化。
当扩展任务正在等待的任何一个事件被设置时,扩展任务将从等待状态移动到就绪状态。
例5.5展示了任务如何设置事件。
多个任务可以等待一个事件。但是,从例5.5中可以看到,事件没有广播机制。换句话说,无法通过单个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(); }
Example 5.5: Setting Events
5.3.1 使用报警器设置事件Setting Events with an Alarm
警报可用于定期激活不终止的扩展任务。每次告警过期,都会设置该事件。等待事件的任务将准备好运行。
5.3.2 通过调度表的Expiry Point设置事件Setting Events with a Schedule Table Expiry Point
调度表上的到期点可用于扩展任务的定期激活。每次处理过期点时,都会设置事件。等待事件的任务将准备好运行。
5.4 清除事件Clearing Events
事件可以由任何任务或ISR设置,但只能由事件所有者清除。
当一个任务等待一个事件时,事件发生了,那么对同一事件的WaitEvent()的后续调用将立即返回,因为事件仍然是设置的。
在等待事件再次发生之前,必须清除该事件最后一次发生的事件。
使用ClearEvent(EventMask) API调用清除事件。EventMask必须与声明的EventMask对应。
例5.6展示了任务通常如何使用ClearEvent()。
当任务终止时,它拥有的所有事件将自动清除。
#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*/ ... } } }
Example 5.6: Clearing Events
5.5 用基本任务模拟扩展任务 Simulating Extended Tasks with Basic Tasks
基本任务只能在任务执行开始或结束时同步。
如果需要其他同步点,则事件机制提供了一种方法。然而,扩展任务通常比基本任务有更大的开销。在资源受限的系统上,只能使用基本任务来构建同步。
例如,如果将任务构建为状态机(例如使用C switch语句),则可以设置状态变量,发出TerminateTask()调用并等待重新激活。例5.7展示了如何实现这一点。
#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(); }
Example 5.7: Multiple Synchronization Points in a Basic Task
5.6 小结
•事件是可以由扩展任务等待的同步对象。
•一个事件可以被多个任务使用。
•设置一个事件不是一个广播机制来通知所有正在等待的任务。
•任务,ISR,Alarm和调度表可以设置事件。
审核编辑:刘清
评论
查看更多