事件Event的含义和作用
本篇內容介紹了“事件Event的含義和作用”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
事件(Event)是一種任務間通信的機制,可用于任務間的同步。多任務環(huán)境下,任務之間往往需要同步操作,一個等待即是一個同步。事件可以提供一對多、多對多的同步操作。本文通過分析鴻蒙輕內核事件模塊的源碼,深入掌握事件的使用。本文中所涉及的源碼,以OpenHarmony LiteOS-M內核為例,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 獲取。
接下來,我們看下事件的結構體,事件初始化,事件常用操作的源代碼。
1、事件結構體定義和常用宏定義
1.1 事件結構體定義
在文件kernel\include\los_event.h定義的事件控制塊結構體為EVENT_CB_S,結構體源代碼如下,結構體成員的解釋見注釋部分。
typedefstructtagEvent{UINT32uwEventID;/**<事件ID,每一位標識一種事件類型*/LOS_DL_LISTstEventList;/**<讀取事件的任務鏈表*/}EVENT_CB_S,*PEVENT_CB_S;
1.2 事件常用宏定義
在讀事件時,可以選擇讀取模式。讀取模式由如下幾個宏定義:
所有事件(LOS_WAITMODE_AND):
邏輯與,基于接口傳入的事件類型掩碼eventMask,只有這些事件都已經(jīng)發(fā)生才能讀取成功,否則該任務將阻塞等待或者返回錯誤碼。
任一事件(LOS_WAITMODE_OR):
邏輯或,基于接口傳入的事件類型掩碼eventMask,只要這些事件中有任一種事件發(fā)生就可以讀取成功,否則該任務將阻塞等待或者返回錯誤碼。
清除事件(LOS_WAITMODE_CLR):
這是一種附加讀取模式,需要與所有事件模式或任一事件模式結合使用(LOS_WAITMODE_AND | LOS_WAITMODE_CLR或 LOS_WAITMODE_OR | LOS_WAITMODE_CLR)。在這種模式下,當設置的所有事件模式或任一事件模式讀取成功后,會自動清除事件控制塊中對應的事件類型位。
#defineLOS_WAITMODE_AND(4)#defineLOS_WAITMODE_OR(2)#defineLOS_WAITMODE_CLR(1)
2、事件常用操作
2.1 初始化事件
在使用事件前,必須使用函數(shù)UINT32 LOS_EventInit(PEVENT_CB_S eventCB)來初始化事件,需要的參數(shù)是結構體指針變量PEVENT_CB_S eventCB。分析下代碼,⑴處表示傳入的參數(shù)不能為空,否則返回錯誤碼。⑵處把事件編碼.uwEventID初始化為0,然后初始化雙向循環(huán)鏈表.stEventList,用于掛載讀取事件的任務。
LITE_OS_SEC_TEXT_INITUINT32LOS_EventInit(PEVENT_CB_SeventCB){⑴if(eventCB==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}⑵eventCB->uwEventID=0;LOS_ListInit(&eventCB->stEventList);OsHookCall(LOS_HOOK_TYPE_EVENT_INIT);returnLOS_OK;}
2.2 校驗事件掩碼
我們可以使用函數(shù)UINT32 LOS_EventPoll(UINT32 *eventId, UINT32 eventMask, UINT32 mode)來校驗事件掩碼,需要的參數(shù)為事件結構體的事件編碼eventId、用戶傳入的待校驗的事件掩碼eventMask及讀取模式mode,返回用戶傳入的事件是否發(fā)生: 返回值為0時,表示用戶預期的事件沒有發(fā)生,否則表示用戶期望的事件發(fā)生。
我們看下源碼,⑴處先檢查傳入?yún)?shù)的合法性,事件編碼不能為空。然后執(zhí)行⑵處的代碼進行校驗。如果是任一事件讀取模式,接下來的判斷不等于表示至少有一個事件發(fā)生了,返回值ret就表示哪些事件發(fā)生了。⑶如果是所有事情讀取模式,當邏輯與運算*eventId & eventMask還等于eventMask時,表示期望的事件全部發(fā)生了,返回值ret就表示哪些事件發(fā)生了。⑷處當ret不為0,期望的事件發(fā)生,并且是清除事件讀取模式時,需要把已經(jīng)發(fā)生的事情進行清除。看來,這個函數(shù)不僅僅是查詢事件有沒有發(fā)生,還會有更新事件編碼的動作。
LITE_OS_SEC_TEXTUINT32LOS_EventPoll(UINT32*eventID,UINT32eventMask,UINT32mode){UINT32ret=0;UINT32intSave;⑴if(eventID==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}intSave=LOS_IntLock();⑵if(mode&LOS_WAITMODE_OR){if((*eventID&eventMask)!=0){ret=*eventID&eventMask;}}else{⑶if((eventMask!=0)&&(eventMask==(*eventID&eventMask))){ret=*eventID&eventMask;}}⑷if(ret&&(mode&LOS_WAITMODE_CLR)){*eventID=*eventID&~(ret);}LOS_IntRestore(intSave);returnret;}
2.3 讀/寫事件
2.3.1 讀取指定事件類型
我們可以使用函數(shù)LOS_EventRead()來讀取事件,需要4個參數(shù)。eventCB是初始化好的事件結構體,eventMask表示需要讀取的事件掩碼,mode是上文說明過的讀取模式,timeout是讀取超時,單位是Tick。函數(shù)返回0時,表示期望的事件沒有發(fā)生,讀取事件失敗,進入阻塞。返回非0時表示期望的事件發(fā)生了,成功讀取事件。下面我們分析下函數(shù)的源碼來看看如何讀取事件的。
⑴處調用函數(shù)OsEventReadParamCheck()進行基礎的校驗,比如第25位保留不能使用,事件掩碼eventMask不能為零,讀取模式組合是否合法。⑵處表示不能中斷中讀取事件。⑶處調用校驗函數(shù)OsEventPoll()檢查事件eventMask是否發(fā)生。如果事件發(fā)生ret不為0,成功讀取直接返回。ret為0,事件沒有發(fā)生時,執(zhí)行⑷,如果超時時間timeout為0,調用者不能等待時,直接返回。⑸如果鎖任務調度時,不能讀取事件,返回錯誤碼。
⑹更新當前任務的阻塞的事件掩碼.eventMask和事件讀取模式.eventMode。執(zhí)行⑺調用函數(shù)OsSchedTaskWait更改當前任務的狀態(tài)為阻塞狀態(tài),掛載到事件的任務阻塞鏈表上。如果timeout不是永久等待,還會把任務設置為OS_TASK_STATUS_PEND_TIME狀態(tài)并設置等待時間。⑻處觸發(fā)任務調度,后續(xù)程序需要等到讀取到事件才會繼續(xù)執(zhí)行。
⑼如果等待時間超時,事件還不可讀,本任務讀取不到指定的事件時,返回錯誤碼。如果可以讀取到指定的事件時,執(zhí)行⑽,檢查事件eventMask是否發(fā)生,然后返回結果值。
LITE_OS_SEC_TEXTUINT32LOS_EventRead(PEVENT_CB_SeventCB,UINT32eventMask,UINT32mode,UINT32timeOut){UINT32ret;UINT32intSave;LosTaskCB*runTsk=NULL;⑴ret=OsEventReadParamCheck(eventCB,eventMask,mode);if(ret!=LOS_OK){returnret;}⑵if(OS_INT_ACTIVE){returnLOS_ERRNO_EVENT_READ_IN_INTERRUPT;}intSave=LOS_IntLock();⑶ret=LOS_EventPoll(&(eventCB->uwEventID),eventMask,mode);OsHookCall(LOS_HOOK_TYPE_EVENT_READ,eventCB,eventMask,mode);if(ret==0){⑷if(timeOut==0){LOS_IntRestore(intSave);returnret;}⑸if(g_losTaskLock){LOS_IntRestore(intSave);returnLOS_ERRNO_EVENT_READ_IN_LOCK;}runTsk=g_losTask.runTask;⑹runTsk->eventMask=eventMask;runTsk->eventMode=mode;⑺OsSchedTaskWait(&eventCB->stEventList,timeOut);LOS_IntRestore(intSave);⑻LOS_Schedule();⑼intSave=LOS_IntLock();if(runTsk->taskStatus&OS_TASK_STATUS_TIMEOUT){runTsk->taskStatus&=~OS_TASK_STATUS_TIMEOUT;LOS_IntRestore(intSave);returnLOS_ERRNO_EVENT_READ_TIMEOUT;}⑽ret=LOS_EventPoll(&eventCB->uwEventID,eventMask,mode);}LOS_IntRestore(intSave);returnret;}
2.3.2 寫入指定的事件類型
我們可以使用函數(shù)UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32 events)來寫入指定的事件類型。代碼如下所示:
下面通過分析源碼來看看如何寫入事件類型的。⑴處代碼把事件結構體的事件掩碼和要寫入的事件類型events進行邏輯或計算,來完成事件的寫入。⑵如果等待事件的任務鏈表不為空,需要處理寫入事件后是否有任務能讀取到相應的事件。⑶處for循環(huán)依次遍歷事件阻塞鏈表上的任務,⑷獲取下一個任務nextTask。⑸處
分不同的讀取模式判斷事件是否符合任務resumedTask讀取事件的要求,如果滿足讀取事件,執(zhí)行⑹設置退出標記exitFlag,然后調用函數(shù)OsSchedTaskWake()把讀取事件的任務更改狀態(tài)并放入就緒隊列,繼續(xù)執(zhí)行⑺,遍歷事件的阻塞任務鏈表中的每一個任務。⑻如果有任務讀取到事件,需要觸發(fā)任務調度。
LITE_OS_SEC_TEXTUINT32LOS_EventWrite(PEVENT_CB_SeventCB,UINT32events){LosTaskCB*resumedTask=NULL;LosTaskCB*nextTask=(LosTaskCB*)NULL;UINT32intSave;UINT8exitFlag=0;if(eventCB==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}if((eventCB->stEventList.pstNext==NULL)||(eventCB->stEventList.pstPrev==NULL)){returnLOS_ERRNO_EVENT_NOT_INITIALIZED;}if(events&LOS_ERRTYPE_ERROR){returnLOS_ERRNO_EVENT_SETBIT_INVALID;}intSave=LOS_IntLock();⑴eventCB->uwEventID|=events;OsHookCall(LOS_HOOK_TYPE_EVENT_WRITE,eventCB);⑵if(!LOS_ListEmpty(&eventCB->stEventList)){⑶for(resumedTask=LOS_DL_LIST_ENTRY((&eventCB->stEventList)->pstNext,LosTaskCB,pendList);&resumedTask->pendList!=(&eventCB->stEventList);){⑷nextTask=LOS_DL_LIST_ENTRY(resumedTask->pendList.pstNext,LosTaskCB,pendList);⑸if(((resumedTask->eventMode&LOS_WAITMODE_OR)&&(resumedTask->eventMask&events)!=0)||((resumedTask->eventMode&LOS_WAITMODE_AND)&&((resumedTask->eventMask&eventCB->uwEventID)==resumedTask->eventMask))){⑹exitFlag=1;OsSchedTaskWake(resumedTask);}⑺resumedTask=nextTask;}if(exitFlag==1){LOS_IntRestore(intSave);⑻LOS_Schedule();returnLOS_OK;}}LOS_IntRestore(intSave);returnLOS_OK;}
2.4 清除事件
我們可以使用函數(shù)UINT32 LOS_EventClear(PEVENT_CB_S eventCB, UINT32 eventMask)來清除指定的事件類型,下面通過分析源碼看看如何清除事件類型的。
函數(shù)參數(shù)為事件結構體eventCB和要清除的事件類型eventMask。清除事件時首先會進行結構體參數(shù)是否為空的校驗,這些比較簡單。⑴處把事件結構體的事件掩碼和要清除的事件類型eventMask進行邏輯與計算,來完成事件的清理。
LITE_OS_SEC_TEXT_MINORUINT32LOS_EventClear(PEVENT_CB_SeventCB,UINT32eventMask){UINT32intSave;if(eventCB==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}intSave=LOS_IntLock();⑴eventCB->uwEventID&=eventMask;LOS_IntRestore(intSave);OsHookCall(LOS_HOOK_TYPE_EVENT_CLEAR,eventCB);returnLOS_OK;}
2.5 銷毀事件
我們可以使用函數(shù)UINT32 LOS_EventDestroy(PEVENT_CB_S eventCB)來銷毀指定的事件控制塊,下面通過分析源碼看看如何銷毀事件的。
函數(shù)參數(shù)為事件結構體,銷毀事件時首先會進行結構體參數(shù)是否為空的校驗,這些比較簡單。⑴處如果事件的任務阻塞鏈表不為空,則不能銷毀事件。⑵把事件結構體的讀取事件的任務鏈表stEventList設置為空,完成事件的銷毀。
LITE_OS_SEC_TEXT_INITUINT32LOS_EventDestroy(PEVENT_CB_SeventCB){UINT32intSave;if(eventCB==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}intSave=LOS_IntLock();⑴if(!LOS_ListEmpty(&eventCB->stEventList)){LOS_IntRestore(intSave);returnLOS_ERRNO_EVENT_SHOULD_NOT_DESTORY;}⑵eventCB->stEventList.pstNext=(LOS_DL_LIST*)NULL;eventCB->stEventList.pstPrev=(LOS_DL_LIST*)NULL;LOS_IntRestore(intSave);OsHookCall(LOS_HOOK_TYPE_EVENT_DESTROY);returnLOS_OK;}
總結
以上是生活随笔為你收集整理的事件Event的含义和作用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: abaqus pythonreader_
- 下一篇: win10找不到网络路径如何解决