溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

事件Event的含義和作用

發(fā)布時(shí)間:2021-06-16 09:59:20 來(lái)源:億速云 閱讀:327 作者:chen 欄目:web開發(fā)

本篇內(nèi)容介紹了“事件Event的含義和作用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

事件(Event)是一種任務(wù)間通信的機(jī)制,可用于任務(wù)間的同步。多任務(wù)環(huán)境下,任務(wù)之間往往需要同步操作,一個(gè)等待即是一個(gè)同步。事件可以提供一對(duì)多、多對(duì)多的同步操作。本文通過(guò)分析鴻蒙輕內(nèi)核事件模塊的源碼,深入掌握事件的使用。本文中所涉及的源碼,以O(shè)penHarmony  LiteOS-M內(nèi)核為例,均可以在開源站點(diǎn)https://gitee.com/openharmony/kernel_liteos_m 獲取。

接下來(lái),我們看下事件的結(jié)構(gòu)體,事件初始化,事件常用操作的源代碼。

1、事件結(jié)構(gòu)體定義和常用宏定義

1.1 事件結(jié)構(gòu)體定義

在文件kernel\include\los_event.h定義的事件控制塊結(jié)構(gòu)體為EVENT_CB_S,結(jié)構(gòu)體源代碼如下,結(jié)構(gòu)體成員的解釋見注釋部分。

typedef struct tagEvent {     UINT32 uwEventID;        /**< 事件ID,每一位標(biāo)識(shí)一種事件類型 */     LOS_DL_LIST stEventList; /**< 讀取事件的任務(wù)鏈表 */ } EVENT_CB_S, *PEVENT_CB_S;

 1.2 事件常用宏定義

在讀事件時(shí),可以選擇讀取模式。讀取模式由如下幾個(gè)宏定義:

所有事件(LOS_WAITMODE_AND):

邏輯與,基于接口傳入的事件類型掩碼eventMask,只有這些事件都已經(jīng)發(fā)生才能讀取成功,否則該任務(wù)將阻塞等待或者返回錯(cuò)誤碼。

任一事件(LOS_WAITMODE_OR):

邏輯或,基于接口傳入的事件類型掩碼eventMask,只要這些事件中有任一種事件發(fā)生就可以讀取成功,否則該任務(wù)將阻塞等待或者返回錯(cuò)誤碼。

清除事件(LOS_WAITMODE_CLR):

這是一種附加讀取模式,需要與所有事件模式或任一事件模式結(jié)合使用(LOS_WAITMODE_AND | LOS_WAITMODE_CLR或  LOS_WAITMODE_OR |  LOS_WAITMODE_CLR)。在這種模式下,當(dāng)設(shè)置的所有事件模式或任一事件模式讀取成功后,會(huì)自動(dòng)清除事件控制塊中對(duì)應(yīng)的事件類型位。

#define LOS_WAITMODE_AND                   (4)    #define LOS_WAITMODE_OR                    (2)    #define LOS_WAITMODE_CLR                   (1)

 2、事件常用操作

2.1 初始化事件

在使用事件前,必須使用函數(shù)UINT32 LOS_EventInit(PEVENT_CB_S  eventCB)來(lái)初始化事件,需要的參數(shù)是結(jié)構(gòu)體指針變量PEVENT_CB_S  eventCB。分析下代碼,⑴處表示傳入的參數(shù)不能為空,否則返回錯(cuò)誤碼。⑵處把事件編碼.uwEventID初始化為0,然后初始化雙向循環(huán)鏈表.stEventList,用于掛載讀取事件的任務(wù)。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_EventInit(PEVENT_CB_S eventCB) { ⑴  if (eventCB == NULL) {         return LOS_ERRNO_EVENT_PTR_NULL;     } ⑵  eventCB->uwEventID = 0;     LOS_ListInit(&eventCB->stEventList);     OsHookCall(LOS_HOOK_TYPE_EVENT_INIT);     return LOS_OK; }

 2.2 校驗(yàn)事件掩碼

我們可以使用函數(shù)UINT32 LOS_EventPoll(UINT32 *eventId, UINT32 eventMask, UINT32  mode)來(lái)校驗(yàn)事件掩碼,需要的參數(shù)為事件結(jié)構(gòu)體的事件編碼eventId、用戶傳入的待校驗(yàn)的事件掩碼eventMask及讀取模式mode,返回用戶傳入的事件是否發(fā)生:  返回值為0時(shí),表示用戶預(yù)期的事件沒(méi)有發(fā)生,否則表示用戶期望的事件發(fā)生。

我們看下源碼,⑴處先檢查傳入?yún)?shù)的合法性,事件編碼不能為空。然后執(zhí)行⑵處的代碼進(jìn)行校驗(yàn)。如果是任一事件讀取模式,接下來(lái)的判斷不等于表示至少有一個(gè)事件發(fā)生了,返回值ret就表示哪些事件發(fā)生了。⑶如果是所有事情讀取模式,當(dāng)邏輯與運(yùn)算*eventId  &  eventMask還等于eventMask時(shí),表示期望的事件全部發(fā)生了,返回值ret就表示哪些事件發(fā)生了。⑷處當(dāng)ret不為0,期望的事件發(fā)生,并且是清除事件讀取模式時(shí),需要把已經(jīng)發(fā)生的事情進(jìn)行清除。看來(lái),這個(gè)函數(shù)不僅僅是查詢事件有沒(méi)有發(fā)生,還會(huì)有更新事件編碼的動(dòng)作。

LITE_OS_SEC_TEXT UINT32 LOS_EventPoll(UINT32 *eventID, UINT32 eventMask, UINT32 mode) {     UINT32 ret = 0;     UINT32 intSave;  ⑴  if (eventID == NULL) {         return LOS_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);     return ret; }

2.3 讀/寫事件

2.3.1 讀取指定事件類型

我們可以使用函數(shù)LOS_EventRead()來(lái)讀取事件,需要4個(gè)參數(shù)。eventCB是初始化好的事件結(jié)構(gòu)體,eventMask表示需要讀取的事件掩碼,mode是上文說(shuō)明過(guò)的讀取模式,timeout是讀取超時(shí),單位是Tick。函數(shù)返回0時(shí),表示期望的事件沒(méi)有發(fā)生,讀取事件失敗,進(jìn)入阻塞。返回非0時(shí)表示期望的事件發(fā)生了,成功讀取事件。下面我們分析下函數(shù)的源碼來(lái)看看如何讀取事件的。

⑴處調(diào)用函數(shù)OsEventReadParamCheck()進(jìn)行基礎(chǔ)的校驗(yàn),比如第25位保留不能使用,事件掩碼eventMask不能為零,讀取模式組合是否合法。⑵處表示不能中斷中讀取事件。⑶處調(diào)用校驗(yàn)函數(shù)OsEventPoll()檢查事件eventMask是否發(fā)生。如果事件發(fā)生ret不為0,成功讀取直接返回。ret為0,事件沒(méi)有發(fā)生時(shí),執(zhí)行⑷,如果超時(shí)時(shí)間timeout為0,調(diào)用者不能等待時(shí),直接返回。⑸如果鎖任務(wù)調(diào)度時(shí),不能讀取事件,返回錯(cuò)誤碼。

⑹更新當(dāng)前任務(wù)的阻塞的事件掩碼.eventMask和事件讀取模式.eventMode。執(zhí)行⑺調(diào)用函數(shù)OsSchedTaskWait更改當(dāng)前任務(wù)的狀態(tài)為阻塞狀態(tài),掛載到事件的任務(wù)阻塞鏈表上。如果timeout不是永久等待,還會(huì)把任務(wù)設(shè)置為OS_TASK_STATUS_PEND_TIME狀態(tài)并設(shè)置等待時(shí)間。⑻處觸發(fā)任務(wù)調(diào)度,后續(xù)程序需要等到讀取到事件才會(huì)繼續(xù)執(zhí)行。

⑼如果等待時(shí)間超時(shí),事件還不可讀,本任務(wù)讀取不到指定的事件時(shí),返回錯(cuò)誤碼。如果可以讀取到指定的事件時(shí),執(zhí)行⑽,檢查事件eventMask是否發(fā)生,然后返回結(jié)果值。

LITE_OS_SEC_TEXT UINT32 LOS_EventRead(PEVENT_CB_S eventCB, UINT32 eventMask, UINT32 mode, UINT32 timeOut) {     UINT32 ret;     UINT32 intSave;     LosTaskCB *runTsk = NULL;  ⑴  ret = OsEventReadParamCheck(eventCB, eventMask, mode);     if (ret != LOS_OK) {         return ret;     }  ⑵  if (OS_INT_ACTIVE) {         return LOS_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);             return ret;         }  ⑸      if (g_losTaskLock) {             LOS_IntRestore(intSave);             return LOS_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);             return LOS_ERRNO_EVENT_READ_TIMEOUT;         }  ⑽      ret = LOS_EventPoll(&eventCB->uwEventID, eventMask, mode);     }      LOS_IntRestore(intSave);     return ret; }

 2.3.2 寫入指定的事件類型

我們可以使用函數(shù)UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32  events)來(lái)寫入指定的事件類型。代碼如下所示:

下面通過(guò)分析源碼來(lái)看看如何寫入事件類型的。⑴處代碼把事件結(jié)構(gòu)體的事件掩碼和要寫入的事件類型events進(jìn)行邏輯或計(jì)算,來(lái)完成事件的寫入。⑵如果等待事件的任務(wù)鏈表不為空,需要處理寫入事件后是否有任務(wù)能讀取到相應(yīng)的事件。⑶處for循環(huán)依次遍歷事件阻塞鏈表上的任務(wù),⑷獲取下一個(gè)任務(wù)nextTask。⑸處

分不同的讀取模式判斷事件是否符合任務(wù)resumedTask讀取事件的要求,如果滿足讀取事件,執(zhí)行⑹設(shè)置退出標(biāo)記exitFlag,然后調(diào)用函數(shù)OsSchedTaskWake()把讀取事件的任務(wù)更改狀態(tài)并放入就緒隊(duì)列,繼續(xù)執(zhí)行⑺,遍歷事件的阻塞任務(wù)鏈表中的每一個(gè)任務(wù)。⑻如果有任務(wù)讀取到事件,需要觸發(fā)任務(wù)調(diào)度。

LITE_OS_SEC_TEXT UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32 events) {     LosTaskCB *resumedTask = NULL;     LosTaskCB *nextTask = (LosTaskCB *)NULL;     UINT32 intSave;     UINT8 exitFlag = 0;     if (eventCB == NULL) {         return LOS_ERRNO_EVENT_PTR_NULL;     }     if ((eventCB->stEventList.pstNext == NULL) || (eventCB->stEventList.pstPrev == NULL)) {         return LOS_ERRNO_EVENT_NOT_INITIALIZED;     }     if (events & LOS_ERRTYPE_ERROR) {         return LOS_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();             return LOS_OK;         }     }      LOS_IntRestore(intSave);     return LOS_OK; }

2.4 清除事件

我們可以使用函數(shù)UINT32 LOS_EventClear(PEVENT_CB_S eventCB, UINT32  eventMask)來(lái)清除指定的事件類型,下面通過(guò)分析源碼看看如何清除事件類型的。

函數(shù)參數(shù)為事件結(jié)構(gòu)體eventCB和要清除的事件類型eventMask。清除事件時(shí)首先會(huì)進(jìn)行結(jié)構(gòu)體參數(shù)是否為空的校驗(yàn),這些比較簡(jiǎn)單。⑴處把事件結(jié)構(gòu)體的事件掩碼和要清除的事件類型eventMask進(jìn)行邏輯與計(jì)算,來(lái)完成事件的清理。

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_EventClear(PEVENT_CB_S eventCB, UINT32 eventMask) {     UINT32 intSave;     if (eventCB == NULL) {         return LOS_ERRNO_EVENT_PTR_NULL;     }     intSave = LOS_IntLock(); ⑴  eventCB->uwEventID &= eventMask;     LOS_IntRestore(intSave);     OsHookCall(LOS_HOOK_TYPE_EVENT_CLEAR, eventCB);     return LOS_OK; }

 2.5 銷毀事件

我們可以使用函數(shù)UINT32 LOS_EventDestroy(PEVENT_CB_S  eventCB)來(lái)銷毀指定的事件控制塊,下面通過(guò)分析源碼看看如何銷毀事件的。

函數(shù)參數(shù)為事件結(jié)構(gòu)體,銷毀事件時(shí)首先會(huì)進(jìn)行結(jié)構(gòu)體參數(shù)是否為空的校驗(yàn),這些比較簡(jiǎn)單。⑴處如果事件的任務(wù)阻塞鏈表不為空,則不能銷毀事件。⑵把事件結(jié)構(gòu)體的讀取事件的任務(wù)鏈表stEventList設(shè)置為空,完成事件的銷毀。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_EventDestroy(PEVENT_CB_S eventCB) {     UINT32 intSave;     if (eventCB == NULL) {         return LOS_ERRNO_EVENT_PTR_NULL;     }     intSave = LOS_IntLock();  ⑴  if (!LOS_ListEmpty(&eventCB->stEventList)) {         LOS_IntRestore(intSave);         return LOS_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);     return LOS_OK; }

“事件Event的含義和作用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI