LiteOS內(nèi)核源碼分析系列十一 軟件定時器Swtmr

      網(wǎng)友投稿 1357 2022-05-30

      LiteOS內(nèi)核源碼分析系列十一 軟件定時器Swtmr

      軟件定時器(Software Timer)是基于系統(tǒng)Tick時鐘中斷且由軟件來模擬的定時器。當經(jīng)過設定的Tick數(shù)后,會觸發(fā)用戶自定義的回調(diào)函數(shù)。硬件定時器受硬件的限制,數(shù)量上不足以滿足用戶的實際需求。Huawei LiteOS提供了軟件定時器功能可以提供更多的定時器,滿足用戶需求。

      本文通過分析LiteOS定時器模塊的源碼,掌握定時器使用上的差異。LiteOS定時器模塊的源代碼,均可以在LiteOS開源站點https://gitee.com/LiteOS/LiteOS 獲取。定時器源代碼、開發(fā)文檔,示例程序代碼如下:

      LiteOS內(nèi)核定時器源代碼

      包括定時器的私有頭文件kernel\base\include\los_swtmr_pri.h、頭文件kernel\include\los_swtmr.h、C源代碼文件kernel\base\los_swtmr.c。

      開發(fā)指南文檔–定時器

      在線文檔https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Kernel_Developer_Guide.md#%E8%BD%AF%E4%BB%B6%E5%AE%9A%E6%97%B6%E5%99%A8。

      接下來,我們看下定時器的結(jié)構體,定時器初始化,定時器常用操作的源代碼。

      1、定時器結(jié)構體定義和常用宏定義

      1.1 定時器結(jié)構體定義

      在文件kernel\base\include\los_swtmr_pri.h定義的定時器控制塊結(jié)構體為LosSwtmrCB,結(jié)構體源代碼如下。定時器狀態(tài).state取值OS_SWTMR_STATUS_UNUSED、OS_SWTMR_STATUS_CREATED或OS_SWTMR_STATUS_TICKING,定時器模式.mode取值LOS_SWTMR_MODE_ONCE、LOS_SWTMR_MODE_PERIOD或LOS_SWTMR_MODE_NO_SELFDELETE。其他結(jié)構體成員的解釋見注釋部分。

      typedef struct { SortLinkList sortList; /**< 定時器排序鏈表 */ UINT8 state; /**< 定時器狀態(tài),取值枚舉SwtmrState */ UINT8 mode; /**< 定時器模式,取值枚舉enSwTmrType */ UINT8 overrun; /**< 周期定時器重復運行的次數(shù) */ UINT16 timerId; /**< 定時器編號Id */ UINT32 interval; /**< 周期定時器超時間隔 (單位: tick) */ UINT32 expiry; /**< 一次性定時器超時間隔 (單位: tick) */ #ifdef LOSCFG_KERNEL_SMP UINT32 cpuid; /**< 定時器運行的CPU */ #endif UINTPTR arg; /**< 定時器超時回調(diào)函數(shù)參數(shù)*/ SWTMR_PROC_FUNC handler; /**< 定時器超時回調(diào)函數(shù) */ } LosSwtmrCB;

      另外,還對回調(diào)函數(shù)及其參數(shù)單獨定義了一個結(jié)構體SwtmrHandlerItem,如下:

      typedef struct { SWTMR_PROC_FUNC handler; /**< 定時器超時回調(diào)函數(shù)參數(shù) */ UINTPTR arg; /**< 定時器超時回調(diào)函數(shù) */ } SwtmrHandlerItem;

      1.2 定時器常用宏定義

      定時器頭文件中還提供了相關的枚舉和宏,從定時器池里獲取定時器控制塊的宏定義OS_SWT_FROM_SID如下:

      #define OS_SWT_FROM_SID(swtmrId) ((LosSwtmrCB *)g_swtmrCBArray + ((swtmrId) % LOSCFG_BASE_CORE_SWTMR_LIMIT))

      文件kernel\base\include\los_swtmr_pri.h中定義的定時器狀態(tài)枚舉SwtmrState,如下:

      enum SwtmrState { OS_SWTMR_STATUS_UNUSED, /**< 定時器未使用 */ OS_SWTMR_STATUS_CREATED, /**< 定時器已創(chuàng)建 */ OS_SWTMR_STATUS_TICKING /**< 定時器計時中 */ };

      文件kernel\include\los_swtmr.h中定義的定時器類型枚舉enSwTmrType,如下:

      enum enSwTmrType { LOS_SWTMR_MODE_ONCE, /**< 一次性定時器, 值為0. */ LOS_SWTMR_MODE_PERIOD, /**< 周期定時器,值為 1. */ LOS_SWTMR_MODE_NO_SELFDELETE, /**< 一次性定時器,不會自刪除,值為2 */ LOS_SWTMR_MODE_OPP /**< 一次性定時器完成后,使能周期性定時器。該模式暫不支持。值為3 */ };

      2、定時器初始化

      定時器在內(nèi)核中默認開啟,用戶可以通過宏LOSCFG_BASE_CORE_SWTMR進行關閉。開啟定時器的情況下,在系統(tǒng)啟動時,在kernel\init\los_init.c中調(diào)用OsSwtmrInit()進行定時器模塊初始化。下面,我們分析下定時器初始化的代碼。

      LiteOS內(nèi)核源碼分析系列十一 軟件定時器Swtmr

      ⑴定時器只在CPU零核上進行初始化,⑵定時器申請內(nèi)存,如果申請失敗,則返回錯誤。⑶初始化雙向循環(huán)鏈表g_swtmrFreeList,維護未使用的定時器。循環(huán)每一個定時器進行初始化,為每一個定時器節(jié)點指定索引timerId,并把定時器節(jié)點插入未使用定時器雙向鏈表g_swtmrFreeList,鏈接用的節(jié)點是.sortList.sortLinkNode。

      宏LOSCFG_BASE_CORE_SWTMR_IN_ISR表示定時器回調(diào)函數(shù)可以在中斷中執(zhí)行,默認關閉。如果開啟的話,定時器回調(diào)直接在中斷中執(zhí)行,不需要創(chuàng)建定時器隊列和定時器任務。在LOSCFG_BASE_CORE_SWTMR_IN_ISR關閉時,執(zhí)行⑷處的代碼為定時器創(chuàng)建隊列,隊列的消息大小OS_SWTMR_HANDLE_QUEUE_SIZE等于定時器的數(shù)量LOSCFG_BASE_CORE_SWTMR_LIMIT,消息內(nèi)容的最大大小為sizeof(CHAR *),4字節(jié),說明隊列消息是指針。后文分析定時器隊列讀取寫入消息的時候具體來看是什么消息。⑸處調(diào)用函數(shù)OsSwtmrTaskCreate()創(chuàng)建定時器任務,定時器任務優(yōu)先級最高,任務的入口函數(shù)為OsSwtmrTask(),后文會分析該函數(shù)。⑹處初始化排序鏈表,源碼分析系列之前的文章分析過,可以閱讀下排序鏈表數(shù)據(jù)結(jié)構章節(jié)。

      LITE_OS_SEC_TEXT_INIT UINT32 OsSwtmrInit(VOID) { UINT32 size; UINT16 index; UINT32 ret; LosSwtmrCB *swtmr = NULL; UINT32 cpuid = ArchCurrCpuid(); ⑴ if (cpuid == 0) { size = sizeof(LosSwtmrCB) * LOSCFG_BASE_CORE_SWTMR_LIMIT; ⑵ swtmr = (LosSwtmrCB *)LOS_MemAlloc(m_aucSysMem0, size); if (swtmr == NULL) { return LOS_ERRNO_SWTMR_NO_MEMORY; } (VOID)memset_s(swtmr, size, 0, size); g_swtmrCBArray = swtmr; ⑶ LOS_ListInit(&g_swtmrFreeList); for (index = 0; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++, swtmr++) { swtmr->timerId = index; LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->sortList.sortLinkNode); } } #ifndef LOSCFG_BASE_CORE_SWTMR_IN_ISR ⑷ ret = LOS_QueueCreate(NULL, OS_SWTMR_HANDLE_QUEUE_SIZE, &g_percpu[cpuid].swtmrHandlerQueue, 0, sizeof(CHAR *)); if (ret != LOS_OK) { return LOS_ERRNO_SWTMR_QUEUE_CREATE_FAILED; } ⑸ ret = OsSwtmrTaskCreate(); if (ret != LOS_OK) { return LOS_ERRNO_SWTMR_TASK_CREATE_FAILED; } #endif ⑹ ret = OsSortLinkInit(&g_percpu[cpuid].swtmrSortLink); if (ret != LOS_OK) { return LOS_ERRNO_SWTMR_SORTLINK_CREATE_FAILED; } return LOS_OK; }

      我們再看一下定時器任務的入口函數(shù)為OsSwtmrTask()。⑴獲取定時器隊列的編號swtmrHandlerQueue,然后進行for永久循環(huán),隊列讀取不到數(shù)據(jù)時會阻塞,因為優(yōu)先級比較高,定時器隊列有數(shù)據(jù)時該任務就會執(zhí)行。從⑵處可以了解,從定時器隊列中讀取的是結(jié)構體的指針地址SwtmrHandlerItemPtr swtmrHandler,讀取的長度為readSize。成功讀取后,獲取定時器回調(diào)函數(shù)及其參數(shù),然后⑷釋放定時器消息占用的內(nèi)存。⑸處執(zhí)行定時器回調(diào)函數(shù)。

      LITE_OS_SEC_TEXT VOID OsSwtmrTask(VOID) { UINT32 ret, swtmrHandlerQueue; SwtmrHandlerItemPtr swtmrHandler = NULL; UINT32 readSize; readSize = sizeof(CHAR *); ⑴ swtmrHandlerQueue = OsPercpuGet()->swtmrHandlerQueue; for (;;) { ⑵ ret = LOS_QueueReadCopy(swtmrHandlerQueue, &swtmrHandler, &readSize, LOS_WAIT_FOREVER); if ((ret == LOS_OK) && (readSize == sizeof(CHAR *))) { ⑶ SWTMR_PROC_FUNC handler = swtmrHandler->handler; UINTPTR arg = swtmrHandler->arg; ⑷ (VOID)LOS_MemFree(m_aucSysMem0, swtmrHandler); if (handler != NULL) { ⑸ handler(arg); } } } }

      3、定時器常用操作

      3.1 定時器創(chuàng)建

      我們分析下創(chuàng)建定時器函數(shù)LOS_SwtmrCreate()的代碼。⑴處對傳入?yún)?shù)定時器超時間隔、定時器模式、回調(diào)函數(shù),定時器編號進行校驗。⑵判斷定時器池是否為空,為空則返回錯誤,無法創(chuàng)建定時器。⑶處如果定時器不為空,則獲取定時器控制塊swtmr。⑷處對定時器控制塊信息進行初始化。⑸處把該定時器排序鏈表節(jié)點的滾動數(shù)初始化為0。

      LITE_OS_SEC_TEXT_INIT UINT32 LOS_SwtmrCreate(UINT32 interval, UINT8 mode, SWTMR_PROC_FUNC handler, UINT16 *swtmrId, UINTPTR arg) { LosSwtmrCB *swtmr = NULL; UINT32 intSave; SortLinkList *sortList = NULL; ⑴ if (interval == 0) { return LOS_ERRNO_SWTMR_INTERVAL_NOT_SUITED; } if ((mode != LOS_SWTMR_MODE_ONCE) && (mode != LOS_SWTMR_MODE_PERIOD) && (mode != LOS_SWTMR_MODE_NO_SELFDELETE)) { return LOS_ERRNO_SWTMR_MODE_INVALID; } if (handler == NULL) { return LOS_ERRNO_SWTMR_PTR_NULL; } if (swtmrId == NULL) { return LOS_ERRNO_SWTMR_RET_PTR_NULL; } SWTMR_LOCK(intSave); ⑵ if (LOS_ListEmpty(&g_swtmrFreeList)) { SWTMR_UNLOCK(intSave); return LOS_ERRNO_SWTMR_MAXSIZE; } ⑶ sortList = LOS_DL_LIST_ENTRY(g_swtmrFreeList.pstNext, SortLinkList, sortLinkNode); swtmr = LOS_DL_LIST_ENTRY(sortList, LosSwtmrCB, sortList); LOS_ListDelete(LOS_DL_LIST_FIRST(&g_swtmrFreeList)); SWTMR_UNLOCK(intSave); ⑷ swtmr->handler = handler; swtmr->mode = mode; swtmr->overrun = 0; swtmr->interval = interval; swtmr->expiry = interval; swtmr->arg = arg; swtmr->state = OS_SWTMR_STATUS_CREATED; ⑸ SET_SORTLIST_VALUE(&(swtmr->sortList), 0); *swtmrId = swtmr->timerId; LOS_TRACE(SWTMR_CREATE, swtmr->timerId); return LOS_OK; }

      3.2 定時器刪除

      我們可以使用函數(shù)LOS_SwtmrDelete(UINT16 swtmrId)來刪除定時器,下面通過分析源碼看看如何刪除定時器的。

      ⑴處判斷定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,如果超過則返回錯誤碼。如果定時器編號沒有問題,獲取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要刪除的定時器swtmrId是否匹配,不匹配則返回錯誤碼。⑶處判斷定時器的狀態(tài),如果定時器定時器沒有創(chuàng)建,不能刪除。如果定時器計時中,需要先停止OsSwtmrStop(swtmr),然后再刪除OsSwtmrDelete(swtmr)。

      LITE_OS_SEC_TEXT UINT32 LOS_SwtmrDelete(UINT16 swtmrId) { LosSwtmrCB *swtmr = NULL; UINT32 intSave; UINT32 ret = LOS_OK; UINT16 swtmrCBId; ⑴ if (swtmrId >= OS_SWTMR_MAX_TIMERID) { return LOS_ERRNO_SWTMR_ID_INVALID; } SWTMR_LOCK(intSave); swtmrCBId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT; swtmr = g_swtmrCBArray + swtmrCBId; ⑵ if (swtmr->timerId != swtmrId) { SWTMR_UNLOCK(intSave); return LOS_ERRNO_SWTMR_ID_INVALID; } ⑶ switch (swtmr->state) { case OS_SWTMR_STATUS_UNUSED: ret = LOS_ERRNO_SWTMR_NOT_CREATED; break; case OS_SWTMR_STATUS_TICKING: OsSwtmrStop(swtmr); /* fall-through */ case OS_SWTMR_STATUS_CREATED: OsSwtmrDelete(swtmr); break; default: ret = LOS_ERRNO_SWTMR_STATUS_INVALID; break; } SWTMR_UNLOCK(intSave); LOS_TRACE(SWTMR_DELETE, swtmr->timerId); return ret; }

      接下來,我們繼續(xù)看看如何調(diào)用函數(shù)OsSwtmrDelete(swtmr)刪除定時器。函數(shù)特別簡單,把定時器放入空閑定時器鏈表g_swtmrFreeList,然后把定時器狀態(tài)改為未使用狀態(tài)就完成了刪除。

      STATIC INLINE VOID OsSwtmrDelete(LosSwtmrCB *swtmr) { LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->sortList.sortLinkNode); swtmr->state = OS_SWTMR_STATUS_UNUSED; }

      3.3 定時器啟動

      創(chuàng)建完畢定時器后,我們可以使用函數(shù)LOS_SwtmrStart(UINT16 swtmrId)來啟動定時器,下面通過分析源碼看看如何啟動定時器的。

      ⑴處判斷定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,如果超過則返回錯誤碼。如果定時器編號沒有問題,獲取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要啟動的定時器swtmrId是否匹配,不匹配則返回錯誤碼。⑶處判斷定時器的狀態(tài),如果定時器定時器沒有創(chuàng)建,不能啟動。如果定時器計時中,需要先停止OsSwtmrStop(swtmr),然后再啟動OsSwtmrStart(swtmr)。

      LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStart(UINT16 swtmrId) { LosSwtmrCB *swtmr = NULL; UINT32 intSave; UINT32 ret = LOS_OK; UINT16 swtmrCBId; ⑴ if (swtmrId >= OS_SWTMR_MAX_TIMERID) { return LOS_ERRNO_SWTMR_ID_INVALID; } SWTMR_LOCK(intSave); swtmrCBId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT; swtmr = g_swtmrCBArray + swtmrCBId; ⑵ if (swtmr->timerId != swtmrId) { SWTMR_UNLOCK(intSave); return LOS_ERRNO_SWTMR_ID_INVALID; } ⑶ switch (swtmr->state) { case OS_SWTMR_STATUS_UNUSED: ret = LOS_ERRNO_SWTMR_NOT_CREATED; break; case OS_SWTMR_STATUS_TICKING: OsSwtmrStop(swtmr); /* fall-through */ case OS_SWTMR_STATUS_CREATED: OsSwtmrStart(swtmr); break; default: ret = LOS_ERRNO_SWTMR_STATUS_INVALID; break; } SWTMR_UNLOCK(intSave); LOS_TRACE(SWTMR_START, swtmr->timerId, swtmr->mode, swtmr->overrun, swtmr->interval, swtmr->expiry); return ret; }

      接下來,我們繼續(xù)看看如何調(diào)用函數(shù)OsSwtmrStart(swtmr)啟動定時器。函數(shù)特別簡單,⑴如果是一次性定時器,把該定時器排序鏈表節(jié)點的滾動數(shù)初始化為.expiry。⑵如果是周期定時器,把該定時器排序鏈表節(jié)點的滾動數(shù)初始化為.interval。⑶然后把該定時器排序鏈表節(jié)點插入超時排序鏈表中,并把定時器狀態(tài)改為計時中。

      LITE_OS_SEC_TEXT VOID OsSwtmrStart(LosSwtmrCB *swtmr) { ⑴ if ((swtmr->overrun == 0) && ((swtmr->mode == LOS_SWTMR_MODE_ONCE) || (swtmr->mode == LOS_SWTMR_MODE_OPP) || (swtmr->mode == LOS_SWTMR_MODE_NO_SELFDELETE))) { SET_SORTLIST_VALUE(&(swtmr->sortList), swtmr->expiry); } else { ⑵ SET_SORTLIST_VALUE(&(swtmr->sortList), swtmr->interval); } ⑶ OsAdd2SortLink(&OsPercpuGet()->swtmrSortLink, &swtmr->sortList); swtmr->state = OS_SWTMR_STATUS_TICKING; #ifdef LOSCFG_KERNEL_SMP swtmr->cpuid = ArchCurrCpuid(); #endif }

      3.4 定時器停止

      我們可以使用函數(shù)LOS_SwtmrStop(UINT16 swtmrId)來停止定時器,下面通過分析源碼看看如何停止定時器的。

      ⑴處判斷定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,如果超過則返回錯誤碼。如果定時器編號沒有問題,獲取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要啟動的定時器swtmrId是否匹配,不匹配則返回錯誤碼。⑶處判斷定時器的狀態(tài),如果定時器定時器沒有創(chuàng)建,沒有啟動,不能停止。如果定時器計時中,會繼續(xù)調(diào)用OsSwtmrStop(swtmr)停止定時器。

      LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStop(UINT16 swtmrId) { LosSwtmrCB *swtmr = NULL; UINT32 intSave; UINT32 ret = LOS_OK; UINT16 swtmrCBId; ⑴ if (swtmrId >= OS_SWTMR_MAX_TIMERID) { return LOS_ERRNO_SWTMR_ID_INVALID; } SWTMR_LOCK(intSave); swtmrCBId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT; swtmr = g_swtmrCBArray + swtmrCBId; ⑵ if (swtmr->timerId != swtmrId) { SWTMR_UNLOCK(intSave); return LOS_ERRNO_SWTMR_ID_INVALID; } ⑶ switch (swtmr->state) { case OS_SWTMR_STATUS_UNUSED: ret = LOS_ERRNO_SWTMR_NOT_CREATED; break; case OS_SWTMR_STATUS_CREATED: ret = LOS_ERRNO_SWTMR_NOT_STARTED; break; case OS_SWTMR_STATUS_TICKING: OsSwtmrStop(swtmr); break; default: ret = LOS_ERRNO_SWTMR_STATUS_INVALID; break; } SWTMR_UNLOCK(intSave); LOS_TRACE(SWTMR_STOP, swtmr->timerId); return ret; }

      接下來,我們繼續(xù)看看如何調(diào)用函數(shù)OsSwtmrStop(swtmr)停止定時器。函數(shù)特別簡單,⑴處首先獲取超時排序鏈表,然后⑵從排序鏈表中刪除該定時器的排序鏈表節(jié)點。最后執(zhí)行⑶更改定時器的狀態(tài)。

      LITE_OS_SEC_TEXT STATIC VOID OsSwtmrStop(LosSwtmrCB *swtmr) { SortLinkAttribute *sortLinkHeader = NULL; #ifdef LOSCFG_KERNEL_SMP sortLinkHeader = &g_percpu[swtmr->cpuid].swtmrSortLink; #else ⑴ sortLinkHeader = &g_percpu[0].swtmrSortLink; #endif ⑵ OsDeleteSortLink(sortLinkHeader, &swtmr->sortList); ⑶ swtmr->state = OS_SWTMR_STATUS_CREATED; swtmr->overrun = 0; }

      4、定時器和Tick時間關系

      定時器加入到超時排序鏈表后,隨時時間一個tick一個tick的逝去,需要不斷的檢查定時器是否超時到期。從之前的文章,已經(jīng)知道系統(tǒng)每走過一個tick,系統(tǒng)就會調(diào)用一次Tick中斷的處理函數(shù)OsTickHandler(),該函數(shù)會調(diào)用定時器掃描函數(shù)OsSwtmrScan()來掃描、更新定時器時間。我們看下OsSwtmrScan()的代碼。

      ⑴處獲取超時排序鏈表,然后更新排序鏈表的游標,獲取超時排序鏈表的鏈表節(jié)點listObject。⑵判斷排序鏈表是否為空,為空則返回。⑶獲取排序鏈表的下一個鏈表節(jié)點sortList,然后把鏈表節(jié)點的滾動數(shù)減少1。⑷循環(huán)遍歷超時排序鏈表上滾動數(shù)為0的鏈表節(jié)點,滾動數(shù)為0,意味著定時器到期,需要處理定時器的匯回調(diào)數(shù)。⑸從超時排序鏈表中刪除超時的節(jié)點,然后獲取定時器控制塊LosSwtmrCB *swtmr。

      當關閉LOSCFG_BASE_CORE_SWTMR_IN_ISR宏時,⑹為定時器回調(diào)函數(shù)結(jié)構體申請內(nèi)存,申請成功則執(zhí)行⑺處代碼,把定時器回調(diào)函數(shù)和參數(shù)的指針賦值給回調(diào)函數(shù)結(jié)構體變量的swtmrHandler的成員。⑻把定時器回調(diào)函數(shù)結(jié)構體變量swtmrHandler的指針寫入定時器隊列,如果寫入失敗會調(diào)用LOS_MemFree(),寫入成功則不會釋放內(nèi)存。⑼處調(diào)用OsSwtmrUpdate(swtmr)更新定時器,稍后分析其代碼。當開啟LOSCFG_BASE_CORE_SWTMR_IN_ISR宏時,不需要把定時器的回調(diào)函數(shù)寫入定時器隊列,直接處理執(zhí)行。⑽處代碼從定時器控制塊中獲取回調(diào)函數(shù)及參數(shù),然后⑾執(zhí)行定時器的回調(diào)函數(shù)。⑿如果超時排序鏈表為空,跳出循環(huán)。否則,獲取排序鏈表上的下一個節(jié)點,繼續(xù)循環(huán)遍歷,來處理同時多個定時器到期的情況。

      LITE_OS_SEC_TEXT VOID OsSwtmrScan(VOID) { SortLinkList *sortList = NULL; LosSwtmrCB *swtmr = NULL; LOS_DL_LIST *listObject = NULL; ⑴ SortLinkAttribute* swtmrSortLink = &OsPercpuGet()->swtmrSortLink; SORTLINK_CURSOR_UPDATE(swtmrSortLink->cursor); SORTLINK_LISTOBJ_GET(listObject, swtmrSortLink); LOS_SpinLock(&g_swtmrSpin); ⑵ if (LOS_ListEmpty(listObject)) { LOS_SpinUnlock(&g_swtmrSpin); return; } ⑶ sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); ROLLNUM_DEC(sortList->idxRollNum); ⑷ while (ROLLNUM(sortList->idxRollNum) == 0) { sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); ⑸ LOS_ListDelete(&sortList->sortLinkNode); swtmr = LOS_DL_LIST_ENTRY(sortList, LosSwtmrCB, sortList); #ifndef LOSCFG_BASE_CORE_SWTMR_IN_ISR LOS_TRACE(SWTMR_EXPIRED, swtmr->timerId); ⑹ SwtmrHandlerItemPtr swtmrHandler = (SwtmrHandlerItemPtr)LOS_MemAlloc(m_aucSysMem0, sizeof(SwtmrHandlerItem)); if (swtmrHandler != NULL) { ⑺ swtmrHandler->handler = swtmr->handler; swtmrHandler->arg = swtmr->arg; ⑻ if (LOS_QueueWriteCopy(OsPercpuGet()->swtmrHandlerQueue, &swtmrHandler, sizeof(CHAR *), LOS_NO_WAIT)) { (VOID)LOS_MemFree(m_aucSysMem0, swtmrHandler); } } ⑼ OsSwtmrUpdate(swtmr); #else ⑽ SWTMR_PROC_FUNC handler = swtmr->handler; UINTPTR arg = swtmr->arg; OsSwtmrUpdate(swtmr); if (handler != NULL) { LOS_SpinUnlock(&g_swtmrSpin); LOS_TRACE(SWTMR_EXPIRED, swtmr->timerId); ⑾ handler(arg); LOS_SpinLock(&g_swtmrSpin); } #endif ⑿ if (LOS_ListEmpty(listObject)) { break; } sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); } LOS_SpinUnlock(&g_swtmrSpin); }

      我們最后看下函數(shù)OsSwtmrUpdate()。⑴處如果是一次性定時器,會把這個定時器刪除,回收到定時器池,狀態(tài)設置為未使用狀態(tài),然后更新定時器的編號timerId。⑵如果是一次性定時器但不刪除,則把定時器狀態(tài)設置為創(chuàng)建狀態(tài)。否則定時器屬于周期性定時器,執(zhí)行⑶記錄定時器執(zhí)行的次數(shù).overrun,然后重新啟動定時器。

      STATIC INLINE VOID OsSwtmrUpdate(LosSwtmrCB *swtmr) { ⑴ if (swtmr->mode == LOS_SWTMR_MODE_ONCE) { OsSwtmrDelete(swtmr); if (swtmr->timerId < (OS_SWTMR_MAX_TIMERID - LOSCFG_BASE_CORE_SWTMR_LIMIT)) { swtmr->timerId += LOSCFG_BASE_CORE_SWTMR_LIMIT; } else { swtmr->timerId %= LOSCFG_BASE_CORE_SWTMR_LIMIT; } ⑵ } else if (swtmr->mode == LOS_SWTMR_MODE_NO_SELFDELETE) { swtmr->state = OS_SWTMR_STATUS_CREATED; } else { ⑶ swtmr->overrun++; OsSwtmrStart(swtmr); } }

      小結(jié)

      本文帶領大家一起剖析了LiteOS定時器模塊的源代碼,包含定時器的結(jié)構體、定時器池初始化、定時器創(chuàng)建、刪除、啟動停止等。感謝閱讀,如有任何問題、建議,都可以留言給我們: https://gitee.com/LiteOS/LiteOS/issues 。為了更容易找到LiteOS代碼倉,建議訪問 https://gitee.com/LiteOS/LiteOS ,關注Watch、Star、并Fork到自己賬戶下,謝謝。

      數(shù)據(jù)結(jié)構 輕量級操作系統(tǒng) LiteOS

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

      上一篇:jQuery Ajax 實例 全解析(轉(zhuǎn)載)
      下一篇:基于華為云IoT設計的健康管理系統(tǒng)并完成應用側(cè)開發(fā)【玩轉(zhuǎn)華為云】
      相關文章
      亚洲精品亚洲人成在线播放| 欧美激情综合亚洲一二区| 亚洲真人日本在线| 亚洲精品自产拍在线观看| 久久久久亚洲精品日久生情| 亚洲日韩人妻第一页| 男人的天堂亚洲一区二区三区| 亚洲AV无码成人网站久久精品大| 亚洲国产精品一区二区成人片国内 | 亚洲欧洲精品国产区| 国产亚洲精品不卡在线| 亚洲精品无码成人片久久不卡 | 久久久久亚洲av无码专区喷水| 亚洲精品韩国美女在线| 亚洲爆乳AAA无码专区| 亚洲视频网站在线观看| 亚洲.国产.欧美一区二区三区| 国产精品亚洲综合一区| 亚洲尹人九九大色香蕉网站| 亚洲最大无码中文字幕| 亚洲中文无码av永久| 中文字幕亚洲图片| 亚洲婷婷国产精品电影人久久| 亚洲国产黄在线观看| 亚洲人成影院77777| 国产亚洲自拍一区| 亚洲精品无码mⅴ在线观看| 亚洲性色精品一区二区在线| 亚洲第一区二区快射影院| 亚洲精品成人无限看| 亚洲成AV人在线播放无码| 久久久久精品国产亚洲AV无码| 亚洲欧洲国产精品你懂的| 亚洲国产精品久久66| 亚洲福利一区二区精品秒拍| 亚洲成av人片一区二区三区| 亚洲成AV人片天堂网无码| 亚洲精品视频在线看| 久久久久亚洲AV成人无码| 亚洲精品动漫人成3d在线| 亚洲一区二区三区免费观看|