正文
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和調度表可以設置事件。
審核編輯:劉清
評論
查看更多