聊聊LiteOS事件模塊的結構體、初始化及常用操作

      網友投稿 761 2022-05-30

      LiteOS內核源碼分析系列九 事件Event

      事件(Event)是一種任務間通信的機制,可用于任務間的同步。多任務環境下,任務之間往往需要同步操作,一個等待即是一個同步。事件可以提供一對多、多對多的同步操作。本文通過分析LiteOS事件模塊的源碼,深入掌握事件的使用。

      LiteOS事件模塊的源代碼,均可以在LiteOS開源站點https://gitee.com/LiteOS/LiteOS 獲取。事件源代碼、開發文檔,示例程序代碼如下:

      LiteOS內核事件源代碼

      包括事件的私有頭文件kernel\base\include\los_event_pri.h、頭文件kernel\include\los_event.h、C源代碼文件kernel\base\los_event.c。

      開發指南文檔–事件

      在線文檔https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Kernel_Developer_Guide.md#%E4%BA%8B%E4%BB%B6。

      接下來,我們看下事件的結構體,事件初始化,事件常用操作的源代碼。

      1、事件結構體定義和常用宏定義

      1.1 事件結構體定義

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

      typedef struct tagEvent { UINT32 uwEventID; /**< 事件ID,每一位標識一種事件類型 */ LOS_DL_LIST stEventList; /**< 讀取事件的任務鏈表 */ } EVENT_CB_S, *PEVENT_CB_S;

      開啟兼容POSIX的宏時LOSCFG_COMPAT_POSIX時,在文件kernel\base\include\los_event_pri.h還定義了結構體EventCond,如下:

      #ifdef LOSCFG_COMPAT_POSIX typedef struct { volatile INT32 *realValue; INT32 value; UINT32 clearEvent; } EventCond;

      1.2 事件常用宏定義

      系統是否支持事件可以通過宏LOSCFG_BASE_IPC_EVENT來配置,默認開啟支持。在讀事件時,可以選擇讀取模式。讀取模式由如下幾個宏定義:

      所有事件(LOS_WAITMODE_AND):

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

      任一事件(LOS_WAITMODE_OR):

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

      清除事件(LOS_WAITMODE_CLR):

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

      #define LOS_WAITMODE_AND 4U #define LOS_WAITMODE_OR 2U #define LOS_WAITMODE_CLR 1U

      3、事件常用操作

      聊聊LiteOS事件模塊的結構體、初始化及常用操作

      3.1 初始化事件

      在使用事件前,必須使用函數UINT32 LOS_EventInit(PEVENT_CB_S eventCB)來初始化事件,需要的參數是結構體PEVENT_CB_S eventCB。分析下代碼,⑴處表示傳入的參數不能為空,否則返回錯誤碼。⑵關中斷后,事件.uwEventID初始化為0,然后初始化雙向循環鏈表.stEventList,用于掛在讀取事件的任務。

      LITE_OS_SEC_TEXT_INIT UINT32 LOS_EventInit(PEVENT_CB_S eventCB) { UINT32 intSave; LOS_TRACE(EVENT_CREATE, (UINTPTR)eventCB); ⑴ if (eventCB == NULL) { return LOS_ERRNO_EVENT_PTR_NULL; } intSave = LOS_IntLock(); ⑵ eventCB->uwEventID = 0; LOS_ListInit(&eventCB->stEventList); LOS_IntRestore(intSave); return LOS_OK; }

      3.2 校驗事件掩碼

      我們可以使用函數UINT32 LOS_EventPoll(UINT32 *eventId, UINT32 eventMask, UINT32 mode)來校驗事件掩碼,需要的參數為事件結構體的事件編碼eventId、用戶傳入的待校驗的事件掩碼eventMask及讀取模式mode,返回用戶傳入的事件是否發生。返回值為0時表示用戶預期的事件沒有發生,否則表示用戶期望的事件發生。我們看下源碼,⑴處先檢查傳入參數的合法性,然后執行⑵處的函數OsEventPoll()進行校驗。

      LITE_OS_SEC_TEXT UINT32 LOS_EventPoll(UINT32 *eventId, UINT32 eventMask, UINT32 mode) { UINT32 ret; UINT32 intSave; ⑴ ret = OsEventParamCheck((VOID *)eventId, eventMask, mode); if (ret != LOS_OK) { return ret; } SCHEDULER_LOCK(intSave); ⑵ ret = OsEventPoll(eventId, eventMask, mode); SCHEDULER_UNLOCK(intSave); return ret; }

      我們繼續看下函數OsEventPoll()。如果是任一事件讀取模式,接下來的判斷不等于表示至少有一個事件發生了,返回值ret就表示哪些事件發生了。⑵如果是所有事情讀取模式,當邏輯與運算*eventId & eventMask還等于eventMask時,表示期望的事件全部發生了,返回值ret就表示哪些事件發生了。⑶當ret不為0,期望的事件發生,并且是清除事件讀取模式時,需要把已經發生的事情進行清除。看來,這個函數不僅僅是查詢事件有沒有發生,還會有更新事件編碼的動作。

      LITE_OS_SEC_TEXT STATIC UINT32 OsEventPoll(UINT32 *eventId, UINT32 eventMask, UINT32 mode) { UINT32 ret = 0; LOS_ASSERT(ArchIntLocked()); LOS_ASSERT(LOS_SpinHeld(&g_taskSpin)); ⑴ 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; } return ret; }

      3.3 讀/寫事件

      3.3.1 讀取指定事件類型

      我們可以使用函數LOS_EventRead()來讀取事件,需要4個參數。eventCB是初始化好的事件結構體,eventMask表示需要讀取的事件掩碼,mode是上文說明過的讀取模式,timeout是讀取超時,單位是Tick。該函數又進一步調用函數OsEventRead()實現事件的讀取,如下:

      LITE_OS_SEC_TEXT UINT32 LOS_EventRead(PEVENT_CB_S eventCB, UINT32 eventMask, UINT32 mode, UINT32 timeout) { return OsEventRead(eventCB, eventMask, mode, timeout, FALSE); }

      下面通過分析函數OsEventRead()的源碼看看如何讀取事件的。函數參數多了個BOOL once,表示是否只讀取一次,對于函數LOS_EventRead(),once取值FALSE。

      ⑴處調用函數OsEventReadCheck()進行基礎的校驗,比如第25位保留不能使用,事件掩碼eventMask不能為零,讀取模式組合是否合法,不能中斷中讀取事件。如果是系統任務讀取事件,會打印警告信息。然后⑵繼續調用函數OsEventReadImp()來讀取事件。

      LITE_OS_SEC_TEXT STATIC UINT32 OsEventRead(PEVENT_CB_S eventCB, UINT32 eventMask, UINT32 mode, UINT32 timeout, BOOL once) { UINT32 ret; UINT32 intSave; ⑴ ret = OsEventReadCheck(eventCB, eventMask, mode); if (ret != LOS_OK) { return ret; } LOS_TRACE(EVENT_READ, (UINTPTR)eventCB, eventCB->uwEventID, eventMask, mode, timeout); SCHEDULER_LOCK(intSave); ⑵ ret = OsEventReadImp(eventCB, eventMask, mode, timeout, once, &intSave); SCHEDULER_UNLOCK(intSave); return ret; }

      我們繼續分析下函數OsEventReadImp()。⑴處當once為假時,調用校驗函數OsEventPoll()檢查事件eventMask是否發生。如果事件發生ret不為0,直接返回。ret為0,事件沒有發生時,執行⑵,如果超時時間timeout為0,調用者不能等待時,直接返回。⑶如果鎖任務調度時,不能讀取事件,返回錯誤碼。

      ⑷更新當前任務的阻塞的事件掩碼.eventMask和事件讀取模式.eventMode。執行⑸,更改當前任務的狀態不再是就緒狀態,設置為阻塞狀態,掛在事件的任務阻塞鏈表上。如果timeout不是永久等待,還會把任務掛在定時器排序鏈表里。⑹處觸發任務調度,后續程序需要等到讀取到事件才會繼續執行。

      ⑺如果等待時間超時,事件還不可讀,本任務讀取不到指定的事件時,返回錯誤碼。如果可以讀取到指定的事件時,執行⑻,檢查事件eventMask是否發生,然后返回結果值。

      LITE_OS_SEC_TEXT STATIC UINT32 OsEventReadImp(PEVENT_CB_S eventCB, UINT32 eventMask, UINT32 mode, UINT32 timeout, BOOL once, UINT32 *intSave) { UINT32 ret = 0; LosTaskCB *runTask = OsCurrTaskGet(); ⑴ if (once == FALSE) { ret = OsEventPoll(&eventCB->uwEventID, eventMask, mode); } ⑵ if (ret == 0) { if (timeout == 0) { return ret; } ⑶ if (!OsPreemptableInSched()) { return LOS_ERRNO_EVENT_READ_IN_LOCK; } ⑷ runTask->eventMask = eventMask; runTask->eventMode = mode; ⑸ OsTaskWait(&eventCB->stEventList, OS_TASK_STATUS_PEND, timeout); ⑹ OsSchedResched(); SCHEDULER_UNLOCK(*intSave); SCHEDULER_LOCK(*intSave); ⑺ if (runTask->taskStatus & OS_TASK_STATUS_TIMEOUT) { runTask->taskStatus &= ~OS_TASK_STATUS_TIMEOUT; return LOS_ERRNO_EVENT_READ_TIMEOUT; } ⑻ ret = OsEventPoll(&eventCB->uwEventID, eventMask, mode); } return ret; }

      3.3.2 寫入指定的事件類型

      我們可以使用函數UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32 events)來寫入指定的事件類型。該函數又進一步調用函數OsEventWrite()實現事件的寫入,如下所示:

      LITE_OS_SEC_TEXT UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32 events) { return OsEventWrite(eventCB, events, FALSE); }

      下面通過分析OsEventWrite()的源碼看看如何寫入事件類型的。函數參數多了個BOOL once,表示是否只讀取一次,對于函數LOS_EventWrite(),once取值FALSE。⑴處代碼把事件結構體的事件掩碼和要寫入的事件類型events進行邏輯或計算,來完成事件的寫入。⑵如果等待事件的任務鏈表不為空,需要處理寫入事件后是否有任務能讀取到相應的事件。⑶處for循環依次遍歷事件阻塞鏈表上的任務,⑷獲取下一個任務nextTask。⑸處

      分不同的讀取模式判斷事件是否符合任務resumedTask讀取事件的要求,如果滿足讀取事件,執行⑹設置退出標記exitFlag,然后調用函數OsTaskWake()把讀取事件的任務更改狀態并放入就緒隊列。⑺處如果參數once為真,則只處理事件的阻塞任務鏈表中的第一個任務。如果為假,繼續執行⑻,遍歷事件的阻塞任務鏈表中的每一個任務。⑼如果有任務讀取到事件,需要觸發任務調度。

      LITE_OS_SEC_TEXT STATIC UINT32 OsEventWrite(PEVENT_CB_S eventCB, UINT32 events, BOOL once) { LosTaskCB *resumedTask = NULL; LosTaskCB *nextTask = NULL; UINT32 intSave; UINT8 exitFlag = 0; if (eventCB == NULL) { return LOS_ERRNO_EVENT_PTR_NULL; } if (events & LOS_ERRTYPE_ERROR) { return LOS_ERRNO_EVENT_SETBIT_INVALID; } LOS_TRACE(EVENT_WRITE, (UINTPTR)eventCB, eventCB->uwEventID, events); SCHEDULER_LOCK(intSave); ⑴ eventCB->uwEventID |= events; ⑵ 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; OsTaskWake(resumedTask, OS_TASK_STATUS_PEND); } ⑺ if (once == TRUE) { break; } ⑻ resumedTask = nextTask; } } SCHEDULER_UNLOCK(intSave); if (exitFlag == 1) { ⑼ LOS_MpSchedule(OS_MP_CPU_ALL); LOS_Schedule(); } return LOS_OK; }

      3.4 清除事件

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

      函數參數為事件結構體eventCB和要清除的事件類型events。清除事件時首先會進行結構體參數是否為空的校驗,這些比較簡單。⑴處把事件結構體的事件掩碼和要清除的事件類型events進行邏輯與計算,來完成事件的清理。

      LITE_OS_SEC_TEXT_MINOR UINT32 LOS_EventClear(PEVENT_CB_S eventCB, UINT32 events) { UINT32 intSave; if (eventCB == NULL) { return LOS_ERRNO_EVENT_PTR_NULL; } LOS_TRACE(EVENT_CLEAR, (UINTPTR)eventCB, eventCB->uwEventID, events); SCHEDULER_LOCK(intSave); ⑴ eventCB->uwEventID &= events; SCHEDULER_UNLOCK(intSave); return LOS_OK; }

      3.5 銷毀事件

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

      函數參數為事件結構體,銷毀事件時首先會進行結構體參數是否為空的校驗,這些比較簡單。⑴處如果事件的任務阻塞鏈表不為空,則不能銷毀事件。⑵把事件結構體的事件掩碼設置為0,完成事件的銷毀。

      LITE_OS_SEC_TEXT_INIT UINT32 LOS_EventDestroy(PEVENT_CB_S eventCB) { UINT32 intSave; UINT32 ret = LOS_OK; if (eventCB == NULL) { return LOS_ERRNO_EVENT_PTR_NULL; } SCHEDULER_LOCK(intSave); ⑴ if (!LOS_ListEmpty(&eventCB->stEventList)) { ret = LOS_ERRNO_EVENT_SHOULD_NOT_DESTORY; goto OUT; } ⑵ eventCB->uwEventID = 0; OUT: SCHEDULER_UNLOCK(intSave); LOS_TRACE(EVENT_DELETE, (UINTPTR)eventCB, ret); return ret; }

      小結

      本文帶領大家一起剖析了LiteOS事件模塊的源代碼,包含事件的結構體、事件初始化、事件創建刪除、申請釋放等。感謝閱讀,如有任何問題、建議,都可以留言給我們: https://gitee.com/LiteOS/LiteOS/issues 。為了更容易找到LiteOS代碼倉,建議訪問 https://gitee.com/LiteOS/LiteOS ,關注Watch、Star、并Fork到自己賬戶下.謝謝。

      數據結構 輕量級操作系統 LiteOS

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Python 前端開發之jQuery綁定事件及操作標簽
      下一篇:基于華為云物聯網設計的智能家居控制系統(STM32+ESP8266)【我們都是華為云專家】
      相關文章
      自拍偷自拍亚洲精品第1页| 伊人久久精品亚洲午夜| 亚洲an天堂an在线观看| 亚洲中文字幕无码久久精品1| 亚洲av无码不卡私人影院| 久久精品熟女亚洲av麻豆| 亚洲成a人片在线观看天堂无码 | 亚洲日本VA中文字幕久久道具| 亚洲综合一区二区| 亚洲白嫩在线观看| 亚洲ts人妖网站| 91丁香亚洲综合社区| 美女视频黄免费亚洲| 亚洲精品无码日韩国产不卡av| 亚洲精品乱码久久久久久蜜桃图片| 亚洲欧洲无卡二区视頻| 久久亚洲中文字幕无码| 国产亚洲视频在线播放大全| 亚洲成a人片在线观看久| 国产精品亚洲综合一区| 亚洲精品无码久久久久sm| 国产成人亚洲精品青草天美| 亚洲丝袜美腿视频| 亚洲欧洲日产专区| 亚洲www77777| 激情小说亚洲色图| 亚洲性日韩精品一区二区三区| 亚洲综合伊人久久大杳蕉| 亚洲av无码av制服另类专区| 久久精品蜜芽亚洲国产AV| 91亚洲视频在线观看| 亚洲色丰满少妇高潮18p| 久久亚洲中文字幕无码| 亚洲精品人成无码中文毛片| 国产亚洲精品国产| 99ri精品国产亚洲| 亚洲综合小说另类图片动图| 国产成人高清亚洲一区久久| 在线观看国产区亚洲一区成人| 亚洲国产精品一区二区久久hs| 亚洲网站在线免费观看|