Events(事件)概述、配置及使用方法

电子说

1.3w人已加入

描述

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调用。下面示例显示了任务如何使用等待事件。

 

#include  


TASK(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),所以用户可以通过按位设置一组位掩码,同时等待多个事件。

当任务等待多个事件时,当等待的任何一个事件发生时,它将恢复。当从等待多个事件恢复时,将需要确定发生了哪些事件。

 

#include  
TASK(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操作系统在关键部分的资源互斥中提供了免于死锁的自由,但在构建具有可能死锁的事件的系统时,仍不会受到保护。如果我们有相互设置和等待事件集的扩展任务,则两个(或更多)任务可能正在等待仅由其他正在等待的任务设置的事件。当然,即使存在死锁扩展任务,系统中的基本任务也不可能死锁。

下面的样例展示了扩展任务的死锁:

 

#include  
TASK(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的调用和对结果的评估之间被抢占,并且被请求的任务的状态在中间时间内可能已经改变。

当扩展任务正在等待的任何一个事件被设置时,扩展任务将从等待状态移动到就绪状态。

如下任务显示了任务如何设置事件:

 

#include  
TASK(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清除。

 

#include  
TASK(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。

如果时效性在系统中很重要,则所有扩展任务(任何等待事件的任务)的优先级必须低于基本任务。

  审核编辑:汤梓红

 

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

全部0条评论

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

×
20
完善资料,
赚取积分