事件(Event)是一種任務(wù)間通信的機制,可用于任務(wù)間的同步。
多任務(wù)環(huán)境下,任務(wù)之間往往需要同步操作,一個等待即是一個同步。事件可以提供一對多、多對多的同步操作。
一對多同步模型:一個任務(wù)等待多個事件的觸發(fā)??梢允侨我庖粋€事件發(fā)生時喚醒任務(wù)處理事件,也可以是幾個事件都發(fā)生后才喚醒任務(wù)處理事件。
多對多同步模型:多個任務(wù)等待多個事件的觸發(fā)。
鴻蒙提供的事件具有如下特點:
任務(wù)通過創(chuàng)建事件控制塊來觸發(fā)事件或等待事件。
事件間相互獨立,內(nèi)部實現(xiàn)為一個32位無符號整型,每一位標識一種事件類型。第25位不可用,因此最多可支持31種事件類型。
事件僅用于任務(wù)間的同步,不提供數(shù)據(jù)傳輸功能。
多次向事件控制塊寫入同一事件類型,在被清零前等效于只寫入一次。
多個任務(wù)可以對同一事件進行讀寫操作。
支持事件讀寫超時機制。
再看事件圖
注意圖中提到了三個概念事件控制塊事件任務(wù)接下來結(jié)合代碼來理解事件模塊的實現(xiàn).
事件控制塊長什么樣?
typedef struct tagEvent { UINT32 uwEventID; /**< Event mask in the event control block,//標識發(fā)生的事件類型位,事件ID,每一位標識一種事件類型 indicating the event that has been logically processed. */ LOS_DL_LIST stEventList; /**< Event control block linked list *///讀取事件任務(wù)鏈表 } EVENT_CB_S, *PEVENT_CB_S;
簡單是簡單,就兩個變量,如下:uwEventID:用于標識該任務(wù)發(fā)生的事件類型,其中每一位表示一種事件類型(0表示該事件類型未發(fā)生、1表示該事件類型已經(jīng)發(fā)生),一共31種事件類型,第25位系統(tǒng)保留。
stEventList,這又是一個雙向鏈表, 雙向鏈表是內(nèi)核最重要的結(jié)構(gòu)體, 可前往鴻蒙內(nèi)核源碼分析 LOS_DL_LIST像狗皮膏藥一樣牢牢的寄生在宿主結(jié)構(gòu)體上stEventList上掛的是所有等待這個事件的任務(wù).
事件控制塊<>事件<>任務(wù) 三者關(guān)系
一定要搞明白這三者的關(guān)系,否則搞不懂事件模塊是如何運作的.
任務(wù)是事件的生產(chǎn)者,通過LOS_EventWrite,向外部廣播發(fā)生了XX事件,并喚醒此前已在事件控制塊中登記過的要等待XX事件發(fā)生的XX任務(wù).
事件控制塊EVENT_CB_S是記錄者,只干兩件事件:
1.uwEventID按位記錄哪些事件發(fā)生了,它只是記錄,怎么消費它不管的.
2.stEventList記錄哪些任務(wù)在等待事件,但任務(wù)究竟在等待哪些事件它也是不記錄的
任務(wù)也是消費者,通過LOS_EventRead消費,只有任務(wù)自己清楚要以什么樣的方式,消費什么樣的事件. 先回顧下任務(wù)結(jié)構(gòu)體LosTaskCB對事件部分的描述如下:
typedef struct { //...去掉不相關(guān)的部分 VOID *taskEvent; //和任務(wù)發(fā)生關(guān)系的事件控制塊 UINT32 eventMask; //對哪些事件進行屏蔽 UINT32 eventMode; //事件三種模式(LOS_WAITMODE_AND,LOS_WAITMODE_OR,LOS_WAITMODE_CLR) } LosTaskCB;
taskEvent指向的就是EVENT_CB_S
eventMask屏蔽掉 事件控制塊 中的哪些事件
eventMode已什么樣的方式去消費事件,三種讀取模式
#define LOS_WAITMODE_AND 4U #define LOS_WAITMODE_OR 2U #define LOS_WAITMODE_CLR 1U
所有事件(LOS_WAITMODE_AND):邏輯與,基于接口傳入的事件類型掩碼eventMask,只有這些事件都已經(jīng)發(fā)生才能讀取成功,否則該任務(wù)將阻塞等待或者返回錯誤碼。
任一事件(LOS_WAITMODE_OR):邏輯或,基于接口傳入的事件類型掩碼eventMask,只要這些事件中有任一種事件發(fā)生就可以讀取成功,否則該任務(wù)將阻塞等待或者返回錯誤碼。
清除事件(LOS_WAITMODE_CLR):這是一種附加讀取模式,需要與所有事件模式或任一事件模式結(jié)合使用(LOS_WAITMODE_AND | LOS_WAITMODE_CLR或LOS_WAITMODE_OR | LOS_WAITMODE_CLR)。在這種模式下,當設(shè)置的所有事件模式或任一事件模式讀取成功后,會自動清除事件控制塊中對應(yīng)的事件類型位。
一個事件控制塊EVENT_CB_S中的事件可以來自多個任務(wù),多個任務(wù)也可以同時消費事件控制塊中的事件,并且這些任務(wù)之間可以沒有任何關(guān)系!
函數(shù)列表
事件可應(yīng)用于多種任務(wù)同步場景,在某些同步場景下可替代信號量。
其中讀懂OsEventWrite和OsEventRead就明白了事件模塊.
事件初始化 -> LOS_EventInit
//初始化一個事件控制塊 LITE_OS_SEC_TEXT_INIT UINT32 LOS_EventInit(PEVENT_CB_S eventCB) { UINT32 intSave; intSave = LOS_IntLock();//鎖中斷 eventCB->uwEventID = 0; //其中每一位表示一種事件類型(0表示該事件類型未發(fā)生、1表示該事件類型已經(jīng)發(fā)生) LOS_ListInit(&eventCB->stEventList);//事件鏈表初始化 LOS_IntRestore(intSave);//恢復(fù)中斷 return LOS_OK; }
代碼解讀:
事件是共享資源,所以操作期間不能產(chǎn)生中斷.
初始化兩個記錄者uwEventIDstEventList
事件生產(chǎn)過程 -> OsEventWrite
LITE_OS_SEC_TEXT VOID OsEventWriteUnsafe(PEVENT_CB_S eventCB, UINT32 events, BOOL once, UINT8 *exitFlag) { LosTaskCB *resumedTask = NULL; LosTaskCB *nextTask = NULL; BOOL schedFlag = FALSE; eventCB->uwEventID |= events;//對應(yīng)位貼上標簽 if (!LOS_ListEmpty(&eventCB->stEventList)) {//等待事件鏈表判斷,處理等待事件的任務(wù) for (resumedTask = LOS_DL_LIST_ENTRY((&eventCB->stEventList)->pstNext, LosTaskCB, pendList); &resumedTask->pendList != &eventCB->stEventList;) {//循環(huán)獲取任務(wù)鏈表 nextTask = LOS_DL_LIST_ENTRY(resumedTask->pendList.pstNext, LosTaskCB, pendList);//獲取任務(wù)實體 if (OsEventResume(resumedTask, eventCB, events)) {//是否恢復(fù)任務(wù) schedFlag = TRUE;//任務(wù)已加至就緒隊列,申請發(fā)生一次調(diào)度 } if (once == TRUE) {//是否只處理一次任務(wù) break;//退出循環(huán) } resumedTask = nextTask;//檢查鏈表中下一個任務(wù) } } if ((exitFlag != NULL) && (schedFlag == TRUE)) {//是否讓外面調(diào)度 *exitFlag = 1; } } //寫入事件 LITE_OS_SEC_TEXT STATIC UINT32 OsEventWrite(PEVENT_CB_S eventCB, UINT32 events, BOOL once) { UINT32 intSave; UINT8 exitFlag = 0; SCHEDULER_LOCK(intSave); //禁止調(diào)度 OsEventWriteUnsafe(eventCB, events, once, &exitFlag);//寫入事件 SCHEDULER_UNLOCK(intSave); //允許調(diào)度 if (exitFlag == 1) { //需要發(fā)生調(diào)度 LOS_MpSchedule(OS_MP_CPU_ALL);//通知所有CPU調(diào)度 LOS_Schedule();//執(zhí)行調(diào)度 } return LOS_OK; }
代碼解讀:
給對應(yīng)位貼上事件標簽,eventCB->uwEventID |= events;注意uwEventID是按位管理的.每個位代表一個事件是否寫入,例如uwEventID = 00010010代表產(chǎn)生了 1,4 事件
循環(huán)從stEventList鏈表中取出等待這個事件的任務(wù)判斷是否喚醒任務(wù).OsEventResume
//事件恢復(fù),判斷是否喚醒任務(wù) LITE_OS_SEC_TEXT STATIC UINT8 OsEventResume(LosTaskCB *resumedTask, const PEVENT_CB_S eventCB, UINT32 events) { UINT8 exitFlag = 0;//是否喚醒 if (((resumedTask->eventMode & LOS_WAITMODE_OR) && ((resumedTask->eventMask & events) != 0)) || ((resumedTask->eventMode & LOS_WAITMODE_AND) && ((resumedTask->eventMask & eventCB->uwEventID) == resumedTask->eventMask))) {//邏輯與 和 邏輯或 的處理 exitFlag = 1; resumedTask->taskEvent = NULL; OsTaskWake(resumedTask);//喚醒任務(wù),加入就緒隊列 } return exitFlag; }
3.喚醒任務(wù)OsTaskWake只是將任務(wù)重新加入就緒隊列,需要立即申請一次調(diào)度LOS_Schedule.
事件消費過程 -> OsEventRead
LITE_OS_SEC_TEXT STATIC UINT32 OsEventRead(PEVENT_CB_S eventCB, UINT32 eventMask, UINT32 mode, UINT32 timeout, BOOL once) { UINT32 ret; UINT32 intSave; SCHEDULER_LOCK(intSave); ret = OsEventReadImp(eventCB, eventMask, mode, timeout, once);//讀事件實現(xiàn)函數(shù) SCHEDULER_UNLOCK(intSave); return ret; } //讀取指定事件類型的實現(xiàn)函數(shù),超時時間為相對時間:單位為Tick LITE_OS_SEC_TEXT STATIC UINT32 OsEventReadImp(PEVENT_CB_S eventCB, UINT32 eventMask, UINT32 mode, UINT32 timeout, BOOL once) { UINT32 ret = 0; LosTaskCB *runTask = OsCurrTaskGet(); runTask->eventMask = eventMask; runTask->eventMode = mode; runTask->taskEvent = eventCB;//事件控制塊 ret = OsTaskWait(&eventCB->stEventList, timeout, TRUE);//任務(wù)進入等待狀態(tài),掛入阻塞鏈表 if (ret == LOS_ERRNO_TSK_TIMEOUT) {//如果返回超時 runTask->taskEvent = NULL; return LOS_ERRNO_EVENT_READ_TIMEOUT; } ret = OsEventPoll(&eventCB->uwEventID, eventMask, mode);//檢測事件是否符合預(yù)期 return ret; }
代碼解讀:
事件控制塊是給任務(wù)使用的, 任務(wù)給出讀取一個事件的條件
eventMask告訴系統(tǒng)屏蔽掉這些事件,對屏蔽的事件不感冒.
eventMode已什么樣的方式去消費事件,是必須都滿足給的條件,還是只滿足一個就響應(yīng).
條件給完后,自己進入等待狀態(tài)OsTaskWait,等待多久timeout決定,任務(wù)自己說了算.
OsEventPoll檢測事件是否符合預(yù)期,啥意思?看下它的代碼就知道了
//根據(jù)用戶傳入的事件值、事件掩碼及校驗?zāi)J?,返回用戶傳入的事件是否符合預(yù)期 LITE_OS_SEC_TEXT UINT32 OsEventPoll(UINT32 *eventID, UINT32 eventMask, UINT32 mode) { UINT32 ret = 0;//事件是否發(fā)生了 LOS_ASSERT(OsIntLocked());//斷言不允許中斷了 LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));//任務(wù)自旋鎖 if (mode & LOS_WAITMODE_OR) {//如果模式是讀取掩碼中任意事件 if ((*eventID & eventMask) != 0) { ret = *eventID & eventMask; //發(fā)生了 } } else {//等待全部事件發(fā)生 if ((eventMask != 0) && (eventMask == (*eventID & eventMask))) {//必須滿足全部事件發(fā)生 ret = *eventID & eventMask; //發(fā)生了 } } if (ret && (mode & LOS_WAITMODE_CLR)) {//是否清除事件 *eventID = *eventID & ~ret; } return ret; }
編程實例
本實例實現(xiàn)如下流程。
示例中,任務(wù)Example_TaskEntry創(chuàng)建一個任務(wù)Example_Event,Example_Event讀事件阻塞,Example_TaskEntry向該任務(wù)寫事件。可以通過示例日志中打印的先后順序理解事件操作時伴隨的任務(wù)切換。
在任務(wù)Example_TaskEntry創(chuàng)建任務(wù)Example_Event,其中任務(wù)Example_Event優(yōu)先級高于Example_TaskEntry。
在任務(wù)Example_Event中讀事件0x00000001,阻塞,發(fā)生任務(wù)切換,執(zhí)行任務(wù)Example_TaskEntry。
在任務(wù)Example_TaskEntry向任務(wù)Example_Event寫事件0x00000001,發(fā)生任務(wù)切換,執(zhí)行任務(wù)Example_Event。
Example_Event得以執(zhí)行,直到任務(wù)結(jié)束。
Example_TaskEntry得以執(zhí)行,直到任務(wù)結(jié)束。
#include "los_event.h" #include "los_task.h" #include "securec.h" /* 任務(wù)ID */ UINT32 g_testTaskId; /* 事件控制結(jié)構(gòu)體 */ EVENT_CB_S g_exampleEvent; /* 等待的事件類型 */ #define EVENT_WAIT 0x00000001 /* 用例任務(wù)入口函數(shù) */ VOID Example_Event(VOID) { UINT32 ret; UINT32 event; /* 超時等待方式讀事件,超時時間為100 ticks, 若100 ticks后未讀取到指定事件,讀事件超時,任務(wù)直接喚醒 */ printf("Example_Event wait event 0x%x \n", EVENT_WAIT); event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND, 100); if (event == EVENT_WAIT) { printf("Example_Event,read event :0x%x\n", event); } else { printf("Example_Event,read event timeout\n"); } } UINT32 Example_TaskEntry(VOID) { UINT32 ret; TSK_INIT_PARAM_S task1; /* 事件初始化 */ ret = LOS_EventInit(&g_exampleEvent); if (ret != LOS_OK) { printf("init event failed .\n"); return -1; } /* 創(chuàng)建任務(wù) */ (VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Event; task1.pcName = "EventTsk1"; task1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE; task1.usTaskPrio = 5; ret = LOS_TaskCreate(&g_testTaskId, &task1); if (ret != LOS_OK) { printf("task create failed .\n"); return LOS_NOK; } /* 寫g_testTaskId 等待事件 */ printf("Example_TaskEntry write event .\n"); ret = LOS_EventWrite(&g_exampleEvent, EVENT_WAIT); if (ret != LOS_OK) { printf("event write failed .\n"); return LOS_NOK; } /* 清標志位 */ printf("EventMask:%d\n", g_exampleEvent.uwEventID); LOS_EventClear(&g_exampleEvent, ~g_exampleEvent.uwEventID); printf("EventMask:%d\n", g_exampleEvent.uwEventID); /* 刪除任務(wù) */ ret = LOS_TaskDelete(g_testTaskId); if (ret != LOS_OK) { printf("task delete failed .\n"); return LOS_NOK; } return LOS_OK; }
運行結(jié)果
Example_Event wait event 0x1 Example_TaskEntry write event . Example_Event,read event :0x1 EventMask:1 EventMask:0 編輯:hfy
-
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2638瀏覽量
66737
發(fā)布評論請先 登錄
相關(guān)推薦
【HarmonyOS】鴻蒙內(nèi)核源碼分析(調(diào)度機制篇)
鴻蒙內(nèi)核源碼分析:用通俗易懂的語言告訴你鴻蒙內(nèi)核發(fā)生了什么?
鴻蒙內(nèi)核源碼分析(源碼注釋篇):給HarmonyOS源碼逐行加上中文注釋
鴻蒙源碼分析系列(總目錄) | 給HarmonyOS源碼逐行加上中文注釋
鴻蒙內(nèi)核源碼分析(必讀篇):用故事說內(nèi)核
鴻蒙內(nèi)核源碼分析(調(diào)度機制篇):Task是如何被調(diào)度執(zhí)行的
鴻蒙內(nèi)核源碼分析(Task管理篇):task是內(nèi)核調(diào)度的單元
基于DSP的實時多任務(wù)調(diào)度內(nèi)核設(shè)計
![基于DSP的實時<b class='flag-5'>多任務(wù)</b>調(diào)度<b class='flag-5'>內(nèi)核</b>設(shè)計](https://file.elecfans.com/web2/M00/4A/15/pYYBAGKhvJWAUsdCAABAMA67_Qk895.png)
鴻蒙內(nèi)核源碼之線程環(huán)境下的任務(wù)切換
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b>之線程<b class='flag-5'>環(huán)境</b><b class='flag-5'>下</b>的<b class='flag-5'>任務(wù)</b>切換](https://file.elecfans.com/web1/M00/EC/D9/pIYBAGCFLQSAKfeTAABVkY3llWc370.png)
鴻蒙內(nèi)核分析:線程中斷環(huán)境下的任務(wù)切換
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>分析</b>:線程中斷<b class='flag-5'>環(huán)境</b><b class='flag-5'>下</b>的<b class='flag-5'>任務(wù)</b>切換](https://file.elecfans.com/web1/M00/E6/49/pIYBAGBURP6AJ00tAAKBis0FJdQ741.png)
鴻蒙內(nèi)核源碼分析 :內(nèi)核最重要結(jié)構(gòu)體
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b><b class='flag-5'>分析</b> :<b class='flag-5'>內(nèi)核</b>最重要結(jié)構(gòu)體](https://file.elecfans.com/web1/M00/D0/79/o4YBAF-81kiAFJ_7AACy6GpuKHw329.png)
華為鴻蒙系統(tǒng)內(nèi)核源碼分析上冊
一種實時嵌入式多任務(wù)微內(nèi)核的分析與改進
![一種實時嵌入式<b class='flag-5'>多任務(wù)</b>微<b class='flag-5'>內(nèi)核</b>的<b class='flag-5'>分析</b>與改進](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論