正文
5.事件Event
在AUTOSAR OS系統(tǒng)中,事件用于向任務(wù)發(fā)送信號(hào)信息。本節(jié)解釋事件是什么,如何配置它們以及如何在運(yùn)行時(shí)使用它們。
事件可用于為擴(kuò)展任務(wù)提供多個(gè)同步點(diǎn)。同步的可視化如圖5.1所示。
擴(kuò)展任務(wù)可以等待事件,這將導(dǎo)致任務(wù)進(jìn)入等待狀態(tài)。當(dāng)系統(tǒng)中的任務(wù)或ISR設(shè)置事件時(shí),等待任務(wù)將進(jìn)入就緒狀態(tài)。當(dāng)它成為最高優(yōu)先級(jí)的就緒任務(wù)時(shí),RTA-OS將選擇它來運(yùn)行。
事件由與其關(guān)聯(lián)的擴(kuò)展任務(wù)擁有。通常,擴(kuò)展任務(wù)將作為一個(gè)無限循環(huán)運(yùn)行,其中包含對(duì)其擁有的事件的一系列受保護(hù)的等待調(diào)用。因此,事件機(jī)制允許構(gòu)建事件驅(qū)動(dòng)的狀態(tài)機(jī)。
如果計(jì)時(shí)行為在您的系統(tǒng)中很重要,那么所有擴(kuò)展任務(wù)(換句話說,等待事件的任何任務(wù))的優(yōu)先級(jí)必須低于基本任務(wù)。
圖5.1 事件的可視化
5.1 配置事件Configuring Event
使用rtaoscfg配置事件。應(yīng)用程序中可以存在的最大事件數(shù)由目標(biāo)硬件決定。查閱目標(biāo)/編譯器端口指南,可以了解每個(gè)任務(wù)可以有多少個(gè)事件。
當(dāng)聲明事件時(shí),它必須具有:
?名字。
? 名稱僅用于在配置時(shí)指示事件的目的。
?至少有一個(gè)任務(wù)使用它。
?事件掩碼。
? 這可以由RTA-OS自動(dòng)分配。
在rtaoscfg中指定的事件名稱在運(yùn)行時(shí)用作事件掩碼的符號(hào)名稱。掩碼是一個(gè)N位向量,其中N是一個(gè)任務(wù)可以等待的最大事件的最大數(shù)量。set位標(biāo)識(shí)一個(gè)特定的事件。
事件名稱在運(yùn)行時(shí)用作掩碼的符號(hào)名稱。通過選擇表示事件的位來配置掩碼。圖5.2顯示了一個(gè)名為WakeUp的事件已經(jīng)被聲明,它將使用事件掩碼中的第9位。除非確實(shí)需要分配一個(gè)特定的位位置,否則最好讓RTA-OS來確定掩碼值。這允許它優(yōu)化bit的打包( optimize the packing of the bits),從而節(jié)省內(nèi)存。
圖5.2:配置事件掩碼
如果一個(gè)事件由多個(gè)任務(wù)使用,則每個(gè)任務(wù)都有自己的單獨(dú)副本。設(shè)置事件時(shí),必須同時(shí)指定任務(wù)。因此,例如,如果您為一個(gè)名為t3的任務(wù)設(shè)置了一個(gè)名為Event2的事件,這對(duì)任務(wù)t4的Event2沒有影響。
5.1.1 定義等待事件 Defining Waiting Tasks
使用rtaoscfg選擇等待任務(wù)。如果聲明一個(gè)等待事件的任務(wù),則意味著該任務(wù)將自動(dòng)被視為擴(kuò)展任務(wù)。
圖5.3顯示了已經(jīng)聲明了一個(gè)事件WakeUp,并且任務(wù)TaskC和TaskD已經(jīng)配置為等待該事件。
等待事件的擴(kuò)展任務(wù)通常會(huì)自動(dòng)啟動(dòng),并且任務(wù)永遠(yuǎn)不會(huì)終止。當(dāng)任務(wù)開始執(zhí)行時(shí),RTA-OS將清除它擁有的所有事件。
圖5.3:選擇等待事件的任務(wù)
5.2 等待事件 Waiting on Events
任務(wù)使用WaitEvent(EventMask) API調(diào)用等待事件。EventMask必須與rtaoscfg中聲明的EventMask對(duì)應(yīng)。
WaitEvent()將事件作為其唯一參數(shù)。當(dāng)調(diào)用執(zhí)行時(shí)有兩種可能:
1)該事件尚未發(fā)生。在這種情況下,任務(wù)將進(jìn)入等待狀態(tài),RTA-OS將在就緒狀態(tài)下運(yùn)行最高優(yōu)先級(jí)的任務(wù)。
2)事件已發(fā)生。在這種情況下,任務(wù)保持在運(yùn)行狀態(tài),并將在WaitEvent()調(diào)用之后的語句中繼續(xù)執(zhí)行。
5.2.1 單個(gè)事件Single Events
等待單個(gè)事件,只需將事件掩碼名稱傳遞給API調(diào)用。示例5.1顯示了任務(wù)如何等待事件。
?
#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操作系統(tǒng)中,為處于掛起狀態(tài)的任務(wù)設(shè)置事件是非法的。在實(shí)踐中,這意味著等待事件的任務(wù)結(jié)構(gòu)通常是一個(gè)等待事件的無限循環(huán),如例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 多個(gè)事件Multiple Events
因?yàn)锳UTOSAR OS事件只是一個(gè)位掩碼,所以可以通過按位順序排列一組位掩碼來同時(shí)等待多個(gè)事件。
當(dāng)任務(wù)等待多個(gè)事件時(shí),當(dāng)?shù)却娜魏我粋€(gè)事件發(fā)生時(shí),它將被恢復(fù)。當(dāng)從等待多個(gè)事件中恢復(fù)時(shí),需要計(jì)算出發(fā)生了哪個(gè)(或多個(gè))事件。
?
#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調(diào)用,允許獲取為任務(wù)設(shè)置的當(dāng)前事件集。
例5.3展示了任務(wù)如何同時(shí)等待多個(gè)事件,然后在恢復(fù)時(shí)識(shí)別哪些事件已被設(shè)置。
5.2.3 擴(kuò)展任務(wù)的死鎖 Deadlock with Extended Tasks
雖然AUTOSAR OS在臨界區(qū)互斥中提供了免于死鎖的方法(參見第4章),但不能避免使用可能死鎖的事件構(gòu)建系統(tǒng)。如果有相互設(shè)置和等待事件集的擴(kuò)展任務(wù),那么可能會(huì)有兩個(gè)(或更多)任務(wù)正在等待事件,而這些事件僅由正在等待的其他任務(wù)設(shè)置。當(dāng)然,系統(tǒng)中的基本任務(wù)不可能死鎖,即使存在死鎖的擴(kuò)展任務(wù)。
例5.4展示了兩個(gè)任務(wù),如果沒有其他任務(wù)設(shè)置Ev1或Ev2,它們將死鎖。
操作系統(tǒng)配置不捕獲哪些任務(wù)/ ISR設(shè)置事件,只捕獲哪些任務(wù)可以等待事件。因此,RTA-OS不可能靜態(tài)地確定擴(kuò)展任務(wù)是否會(huì)死鎖。然而,以下設(shè)計(jì)方法可能會(huì)有所幫助:
?只使用基本任務(wù)
?分析代碼,以顯示在所有SetEvent()/WaitEvent()對(duì)的傳遞性閉包上沒有循環(huán)等待事件
?
#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 設(shè)置事件Setting Events
使用SetEvent() API設(shè)置事件。
SetEvent()調(diào)用有兩個(gè)參數(shù),一個(gè)任務(wù)和一個(gè)事件掩碼。對(duì)于指定的任務(wù),SetEvent()調(diào)用設(shè)置事件掩碼中指定的事件。該調(diào)用不為共享事件的任何其他任務(wù)設(shè)置事件??梢栽谡{(diào)用SetEvent()時(shí)按位或多個(gè)事件掩碼,以同時(shí)為任務(wù)設(shè)置多個(gè)事件
無法為處于掛起狀態(tài)的任務(wù)設(shè)置事件。因此,在設(shè)置事件之前,必須確保任務(wù)沒有掛起。
可以使用GetTaskState() API調(diào)用來實(shí)現(xiàn)這一點(diǎn),但請(qǐng)注意,當(dāng)對(duì)比調(diào)用者優(yōu)先級(jí)更高的任務(wù)調(diào)用此方法時(shí),存在潛在的競(jìng)爭(zhēng)條件。調(diào)用者可能在調(diào)用API和評(píng)估結(jié)果之間被搶占,并且被請(qǐng)求的任務(wù)的狀態(tài)可能在這段時(shí)間內(nèi)發(fā)生了變化。
當(dāng)擴(kuò)展任務(wù)正在等待的任何一個(gè)事件被設(shè)置時(shí),擴(kuò)展任務(wù)將從等待狀態(tài)移動(dòng)到就緒狀態(tài)。
例5.5展示了任務(wù)如何設(shè)置事件。
多個(gè)任務(wù)可以等待一個(gè)事件。但是,從例5.5中可以看到,事件沒有廣播機(jī)制。換句話說,無法通過單個(gè)API調(diào)用向等待該事件的所有任務(wù)發(fā)出事件發(fā)生的信號(hào)。事件也可以通過鬧鐘和時(shí)間表來設(shè)置。
?
#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 使用報(bào)警器設(shè)置事件Setting Events with an Alarm
警報(bào)可用于定期激活不終止的擴(kuò)展任務(wù)。每次告警過期,都會(huì)設(shè)置該事件。等待事件的任務(wù)將準(zhǔn)備好運(yùn)行。
5.3.2 通過調(diào)度表的Expiry Point設(shè)置事件Setting Events with a Schedule Table Expiry Point
調(diào)度表上的到期點(diǎn)可用于擴(kuò)展任務(wù)的定期激活。每次處理過期點(diǎn)時(shí),都會(huì)設(shè)置事件。等待事件的任務(wù)將準(zhǔn)備好運(yùn)行。
5.4 清除事件Clearing Events
事件可以由任何任務(wù)或ISR設(shè)置,但只能由事件所有者清除。
當(dāng)一個(gè)任務(wù)等待一個(gè)事件時(shí),事件發(fā)生了,那么對(duì)同一事件的WaitEvent()的后續(xù)調(diào)用將立即返回,因?yàn)槭录匀皇窃O(shè)置的。
在等待事件再次發(fā)生之前,必須清除該事件最后一次發(fā)生的事件。
使用ClearEvent(EventMask) API調(diào)用清除事件。EventMask必須與聲明的EventMask對(duì)應(yīng)。
例5.6展示了任務(wù)通常如何使用ClearEvent()。
當(dāng)任務(wù)終止時(shí),它擁有的所有事件將自動(dòng)清除。
?
#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 用基本任務(wù)模擬擴(kuò)展任務(wù) Simulating Extended Tasks with Basic Tasks
基本任務(wù)只能在任務(wù)執(zhí)行開始或結(jié)束時(shí)同步。
如果需要其他同步點(diǎn),則事件機(jī)制提供了一種方法。然而,擴(kuò)展任務(wù)通常比基本任務(wù)有更大的開銷。在資源受限的系統(tǒng)上,只能使用基本任務(wù)來構(gòu)建同步。
例如,如果將任務(wù)構(gòu)建為狀態(tài)機(jī)(例如使用C switch語句),則可以設(shè)置狀態(tài)變量,發(fā)出TerminateTask()調(diào)用并等待重新激活。例5.7展示了如何實(shí)現(xiàn)這一點(diǎn)。
?
#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 小結(jié)
?事件是可以由擴(kuò)展任務(wù)等待的同步對(duì)象。
?一個(gè)事件可以被多個(gè)任務(wù)使用。
?設(shè)置一個(gè)事件不是一個(gè)廣播機(jī)制來通知所有正在等待的任務(wù)。
?任務(wù),ISR,Alarm和調(diào)度表可以設(shè)置事件。
審核編輯:劉清
評(píng)論
查看更多