摘要:本文經過分析鴻蒙輕內核定時器模塊的源碼,掌握定時器使用上的差別。
本文分享自華爲雲社區《鴻蒙輕內核M核源碼分析系列十四 軟件定時器Swtmr》,做者:zhushy 。git
軟件定時器(Software Timer)是基於系統Tick時鐘中斷且由軟件來模擬的定時器。當通過設定的Tick數後,會觸發用戶自定義的回調函數。硬件定時器受硬件的限制,數量上不足以知足用戶的實際需求。鴻蒙輕內核提供了軟件定時器功能能夠提供更多的定時器,知足用戶需求。數組
本文經過分析鴻蒙輕內核定時器模塊的源碼,掌握定時器使用上的差別。本文中所涉及的源碼,以OpenHarmony LiteOS-M內核爲例,都可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 獲取。數據結構
接下來,咱們看下定時器的結構體,定時器初始化,定時器經常使用操做的源代碼。函數
在文件kernel\include\los_swtmr.h定義的定時器控制塊結構體爲SWTMR_CTRL_S,結構體源代碼以下。定時器狀態.ucState取值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。其餘結構體成員的解釋見註釋部分。源碼分析
typedef struct tagSwTmrCtrl { struct tagSwTmrCtrl *pstNext; /* 指向下一個定時器結構體的指針 */ UINT8 ucState; /* 定時器狀態,取值枚舉SwtmrState */ UINT8 ucMode; /* 定時器模式,取值枚舉enSwTmrType */ #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) UINT8 ucRouses; /* 喚醒開關 */ UINT8 ucSensitive; /* 對齊開關 */ #endif UINT32 usTimerID; /* 定時器編號Id */ UINT32 uwCount; /* 定時器運行的次數 */ UINT32 uwInterval; /* 週期定時器超時間隔 (單位: tick) */ UINT32 uwArg; /* 定時器超時回調函數參數 */ SWTMR_PROC_FUNC pfnHandler; /* 定時器超時回調函數 */ SortLinkList stSortList; /* 定時器排序鏈表 */ } SWTMR_CTRL_S;
另外,還對回調函數及其參數單獨定義了一個結構體SwtmrHandlerItem,以下:spa
typedef struct { SWTMR_PROC_FUNC handler; /**< 定時器超時回調函數 */ UINTPTR arg; /**< 定時器超時回調函數參數 */ } SwtmrHandlerItem;
定時器頭文件kernel\include\los_swtmr.h中還提供了相關的枚舉和宏,從定時器池裏獲取定時器控制塊的宏定義OS_SWT_FROM_SID以下:指針
#define OS_SWT_FROM_SID(swtmrId) ((SWTMR_CTRL_S *)g_swtmrCBArray + ((swtmrId) % LOSCFG_BASE_CORE_SWTMR_LIMIT))
頭文件中定義的定時器幾個枚舉以下:
enum SwtmrState { OS_SWTMR_STATUS_UNUSED, /**< 定時器未使用 */ OS_SWTMR_STATUS_CREATED, /**< 定時器已建立 */ OS_SWTMR_STATUS_TICKING /**< 定時器計時中 */ }; #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) enum enSwTmrRousesType { OS_SWTMR_ROUSES_IGNORE, /* 定時器不能喚醒系統 */ OS_SWTMR_ROUSES_ALLOW, /* 定時器能喚醒系統 */ }; enum enSwTmrAlignSensitive { OS_SWTMR_ALIGN_SENSITIVE, /* 定時器不須要對齊 */ OS_SWTMR_ALIGN_INSENSITIVE, /* 定時器須要對齊 */ }; #endif enum EnSwTmrType { LOS_SWTMR_MODE_ONCE, /* 一次性定時器, 值爲0. */ LOS_SWTMR_MODE_PERIOD, /* 週期定時器,值爲 1. */ LOS_SWTMR_MODE_NO_SELFDELETE, /* 一次性定時器,不會自刪除,值爲2 */ LOS_SWTMR_MODE_OPP, /* 一次性定時器完成後,使能週期性定時器。該模式暫不支持。值爲3 */ };
定時器在內核中默認開啓,用戶能夠經過宏LOSCFG_BASE_CORE_SWTMR進行關閉。開啓定時器的狀況下,在系統啓動時,在kernel\src\los_init.c中調用OsSwtmrInit()進行定時器模塊初始化。下面,咱們分析下定時器初始化的代碼。code
⑴處若是開啓定時器對齊宏LOSCFG_BASE_CORE_SWTMR_ALIGN,清零g_swtmrAlignID數組。定時器的數量由宏LOSCFG_BASE_CORE_SWTMR_LIMIT定義,⑵處計算定時器池須要的內存大小,而後爲定時器申請內存,若是申請失敗,則返回錯誤。⑶初始化空閒定時器鏈表g_swtmrFreeList,維護未使用的定時器。循環每個定時器進行初始化,爲每個定時器節點指定索引timerId,定時器控制塊依次指向下一個定時器控制塊。blog
⑷處代碼爲定時器建立隊列,隊列的消息大小OS_SWTMR_HANDLE_QUEUE_SIZE等於定時器的數量LOSCFG_BASE_CORE_SWTMR_LIMIT,消息內容的最大大小sizeof(SwtmrHandlerItem)。後文分析定時器隊列讀取寫入消息的時候具體來看是什麼消息。⑸處調用函數OsSwtmrTaskCreate()建立定時器任務,定時器任務優先級最高,任務的入口函數爲OsSwtmrTask(),後文會分析該函數。⑹處初始化定時器排序鏈表,源碼分析系列以前的文章分析過,能夠閱讀下排序鏈表數據結構章節。⑺處註冊定時器掃描函數OsSwtmrScan。排序
LITE_OS_SEC_TEXT_INIT UINT32 OsSwtmrInit(VOID) { UINT32 size; UINT16 index; UINT32 ret; #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) // Ignore the return code when matching CSEC rule 6.6(1). ⑴ (VOID)memset_s((VOID *)g_swtmrAlignID, sizeof(SwtmrAlignData) * LOSCFG_BASE_CORE_SWTMR_LIMIT, 0, sizeof(SwtmrAlignData) * LOSCFG_BASE_CORE_SWTMR_LIMIT); #endif ⑵ size = sizeof(SWTMR_CTRL_S) * LOSCFG_BASE_CORE_SWTMR_LIMIT; SWTMR_CTRL_S *swtmr = (SWTMR_CTRL_S *)LOS_MemAlloc(m_aucSysMem0, size); if (swtmr == NULL) { return LOS_ERRNO_SWTMR_NO_MEMORY; } // Ignore the return code when matching CSEC rule 6.6(3). (VOID)memset_s((VOID *)swtmr, size, 0, size); g_swtmrCBArray = swtmr; ⑶ g_swtmrFreeList = swtmr; swtmr->usTimerID = 0; SWTMR_CTRL_S *temp = swtmr; swtmr++; for (index = 1; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++, swtmr++) { swtmr->usTimerID = index; temp->pstNext = swtmr; temp = swtmr; } ⑷ ret = LOS_QueueCreate((CHAR *)NULL, OS_SWTMR_HANDLE_QUEUE_SIZE, &g_swtmrHandlerQueue, 0, sizeof(SwtmrHandlerItem)); if (ret != LOS_OK) { (VOID)LOS_MemFree(m_aucSysMem0, swtmr); return LOS_ERRNO_SWTMR_QUEUE_CREATE_FAILED; } ⑸ ret = OsSwtmrTaskCreate(); if (ret != LOS_OK) { (VOID)LOS_MemFree(m_aucSysMem0, swtmr); return LOS_ERRNO_SWTMR_TASK_CREATE_FAILED; } ⑹ g_swtmrSortLinkList = OsGetSortLinkAttribute(OS_SORT_LINK_SWTMR); if (g_swtmrSortLinkList == NULL) { (VOID)LOS_MemFree(m_aucSysMem0, swtmr); return LOS_NOK; } ret = OsSortLinkInit(g_swtmrSortLinkList); if (ret != LOS_OK) { (VOID)LOS_MemFree(m_aucSysMem0, swtmr); return LOS_NOK; } ⑺ ret = OsSchedSwtmrScanRegister((SchedScan)OsSwtmrScan); if (ret != LOS_OK) { (VOID)LOS_MemFree(m_aucSysMem0, swtmr); return LOS_NOK; } return LOS_OK; }
咱們再看一下定時器任務的入口函數爲OsSwtmrTask()。⑴進行for永久循環,隊列讀取不到數據時會阻塞,由於優先級比較高,定時器隊列有數據時該任務就會執行。從定時器隊列中讀取定時器處理函數地址放入指針地址&swtmrHandle,讀取的長度爲sizeof(SwtmrHandlerItem)。成功讀取後,獲取定時器回調函數及其參數,而後⑵處執行定時器回調函數。記錄定時器回調函數的執行時間,⑶處判斷執行時間是否超時,若是超時,打印警告信息。
LITE_OS_SEC_TEXT VOID OsSwtmrTask(VOID) { SwtmrHandlerItem swtmrHandle; UINT32 readSize; UINT32 ret; UINT64 tick; readSize = sizeof(SwtmrHandlerItem); for (;;) { ⑴ ret = LOS_QueueReadCopy(g_swtmrHandlerQueue, &swtmrHandle, &readSize, LOS_WAIT_FOREVER); if ((ret == LOS_OK) && (readSize == sizeof(SwtmrHandlerItem))) { if (swtmrHandle.handler == NULL) { continue; } tick = LOS_TickCountGet(); ⑵ swtmrHandle.handler(swtmrHandle.arg); tick = LOS_TickCountGet() - tick; ⑶ if (tick >= SWTMR_MAX_RUNNING_TICKS) { PRINT_WARN("timer_handler(%p) cost too many ms(%d)\n", swtmrHandle.handler, (UINT32)((tick * OS_SYS_MS_PER_SECOND) / LOSCFG_BASE_CORE_TICK_PER_SECOND)); } } } }
咱們分析下建立定時器函數LOS_SwtmrCreate()的代碼。先不考慮定時器對齊LOSCFG_BASE_CORE_SWTMR_ALIGN的狀況。先看下函數參數,interval是定時器執行時間間隔,mode是建立的定時器模式,handler、arg是定時器回調函數及其參數。swtmrId是定時器編號。
⑴處對傳入參數定時器超時間隔、定時器模式、回調函數,定時器編號進行校驗。⑵判斷空閒定時器池是否爲空,爲空則返回錯誤,沒法建立定時器。⑶處若是定時器不爲空,則獲取定時器控制塊swtmr。⑷處對定時器控制塊信息進行初始化。⑸處把該定時器排序鏈表節點的響應時間responseTime初始化爲-1。
#if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) LITE_OS_SEC_TEXT_INIT UINT32 LOS_SwtmrCreate(UINT32 interval, UINT8 mode, SWTMR_PROC_FUNC handler, UINT32 *swtmrId, UINT32 arg, UINT8 rouses, UINT8 sensitive) #else LITE_OS_SEC_TEXT_INIT UINT32 LOS_SwtmrCreate(UINT32 interval, UINT8 mode, SWTMR_PROC_FUNC handler, UINT32 *swtmrId, UINT32 arg) #endif { SWTMR_CTRL_S *swtmr = NULL; UINT32 intSave; ⑴ 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; } #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) if ((rouses != OS_SWTMR_ROUSES_IGNORE) && (rouses != OS_SWTMR_ROUSES_ALLOW)) { return OS_ERRNO_SWTMR_ROUSES_INVALID; } if ((sensitive != OS_SWTMR_ALIGN_INSENSITIVE) && (sensitive != OS_SWTMR_ALIGN_SENSITIVE)) { return OS_ERRNO_SWTMR_ALIGN_INVALID; } #endif intSave = LOS_IntLock(); ⑵ if (g_swtmrFreeList == NULL) { LOS_IntRestore(intSave); return LOS_ERRNO_SWTMR_MAXSIZE; } ⑶ swtmr = g_swtmrFreeList; g_swtmrFreeList = swtmr->pstNext; LOS_IntRestore(intSave); ⑷ swtmr->pfnHandler = handler; swtmr->ucMode = mode; swtmr->uwInterval = interval; swtmr->pstNext = (SWTMR_CTRL_S *)NULL; swtmr->uwCount = 0; swtmr->uwArg = arg; #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) swtmr->ucRouses = rouses; swtmr->ucSensitive = sensitive; #endif swtmr->ucState = OS_SWTMR_STATUS_CREATED; *swtmrId = swtmr->usTimerID; ⑸ SET_SORTLIST_VALUE(&swtmr->stSortList, OS_SORT_LINK_INVALID_TIME); return LOS_OK; }
咱們可使用函數LOS_SwtmrDelete(UINT32 swtmrId)來刪除定時器,下面經過分析源碼看看如何刪除定時器的。
⑴處判判定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,若是超過則返回錯誤碼。若是定時器編號沒有問題,獲取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要刪除的定時器swtmrId是否匹配,不匹配則返回錯誤碼。⑶處判判定時器的狀態,若是定時器定時器沒有建立,不能刪除。若是定時器計時中,須要先中止OsSwtmrStop(swtmr),而後再刪除OsSwtmrDelete(swtmr)。
LITE_OS_SEC_TEXT UINT32 LOS_SwtmrDelete(UINT32 swtmrId) { SWTMR_CTRL_S *swtmr = NULL; UINT32 intSave; UINT32 ret = LOS_OK; UINT16 swtmrCbId; ⑴ if (swtmrId >= OS_SWTMR_MAX_TIMERID) { return LOS_ERRNO_SWTMR_ID_INVALID; } intSave = LOS_IntLock(); swtmrCbId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT; swtmr = g_swtmrCBArray + swtmrCbId; ⑵ if (swtmr->usTimerID != swtmrId) { LOS_IntRestore(intSave); return LOS_ERRNO_SWTMR_ID_INVALID; } ⑶ switch (swtmr->ucState) { 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; } LOS_IntRestore(intSave); return ret; }
接下來,咱們繼續看看如何調用函數OsSwtmrDelete(swtmr)刪除定時器。函數特別簡單,把定時器放入空閒定時器鏈表g_swtmrFreeList頭部,而後把定時器狀態改成未使用狀態就完成了刪除。
STATIC_INLINE VOID OsSwtmrDelete(SWTMR_CTRL_S *swtmr) { /* insert to free list */ swtmr->pstNext = g_swtmrFreeList; g_swtmrFreeList = swtmr; swtmr->ucState = OS_SWTMR_STATUS_UNUSED; #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) (VOID)memset_s((VOID *)&g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT], sizeof(SwtmrAlignData), 0, sizeof(SwtmrAlignData)); #endif }
建立完畢定時器後,咱們可使用函數LOS_SwtmrStart(UINT32 swtmrId)來啓動定時器,下面經過分析源碼看看如何啓動定時器的。
⑴處判判定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,若是超過則返回錯誤碼。若是定時器編號沒有問題,獲取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要啓動的定時器swtmrId是否匹配,不匹配則返回錯誤碼。⑶處判判定時器的狀態,若是定時器定時器沒有建立,不能啓動。若是定時器計時中,須要先中止OsSwtmrStop(swtmr),而後再啓動OsSwtmrStart(swtmr)。
LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStart(UINT32 swtmrId) { UINT32 intSave; UINT32 ret = LOS_OK; ⑴ if (swtmrId >= OS_SWTMR_MAX_TIMERID) { return LOS_ERRNO_SWTMR_ID_INVALID; } intSave = LOS_IntLock(); SWTMR_CTRL_S *swtmr = g_swtmrCBArray + swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT; ⑵ if (swtmr->usTimerID != swtmrId) { LOS_IntRestore(intSave); return LOS_ERRNO_SWTMR_ID_INVALID; } #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) if ((swtmr->ucSensitive == OS_SWTMR_ALIGN_INSENSITIVE) && (swtmr->ucMode == LOS_SWTMR_MODE_PERIOD)) { UINT32 swtmrAlignIdIndex = swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT; g_swtmrAlignID[swtmrAlignIdIndex].canAlign = 1; if ((swtmr->uwInterval % LOS_COMMON_DIVISOR) == 0) { g_swtmrAlignID[swtmrAlignIdIndex].canMultiple = 1; g_swtmrAlignID[swtmrAlignIdIndex].times = swtmr->uwInterval / LOS_COMMON_DIVISOR; } } #endif ⑶ switch (swtmr->ucState) { 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; } LOS_IntRestore(intSave); return ret; }
接下來,咱們繼續看看如何調用函數OsSwtmrStart(swtmr)啓動定時器。函數特別簡單,⑴設置定時器的等待超時時間,並把定時器狀態改成計時中。⑵處把該定時器插入超時排序鏈表中。若是已使能任務調度,則修改過時時間。
LITE_OS_SEC_TEXT VOID OsSwtmrStart(SWTMR_CTRL_S *swtmr) { UINT64 currTime = OsGetCurrSchedTimeCycle(); ⑴ swtmr->uwCount = swtmr->uwInterval; swtmr->ucState = OS_SWTMR_STATUS_TICKING; #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) if ((g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT].canAlign == 1) && (g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT].isAligned == 0)) { g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT].isAligned = 1; OsSwtmrFindAlignPos(currTime, swtmr); } #endif ⑵ OsAdd2SortLink(&swtmr->stSortList, currTime, swtmr->uwCount, OS_SORT_LINK_SWTMR); if (LOS_TaskIsRunning()) { ⑶ OsSchedUpdateExpireTime(currTime); } }
咱們可使用函數LOS_SwtmrStop(UINT32 swtmrId)來中止定時器,下面經過分析源碼看看如何中止定時器的。
⑴處判判定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,若是超過則返回錯誤碼。若是定時器編號沒有問題,獲取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要啓動的定時器swtmrId是否匹配,不匹配則返回錯誤碼。⑶處判判定時器的狀態,若是定時器定時器沒有建立,沒有啓動,不能中止。若是定時器計時中,會繼續調用OsSwtmrStop(swtmr)中止定時器。
LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStop(UINT32 swtmrId) { SWTMR_CTRL_S *swtmr = NULL; UINT32 intSave; UINT16 swtmrCbId; UINT32 ret = LOS_OK; ⑴ if (swtmrId >= OS_SWTMR_MAX_TIMERID) { return LOS_ERRNO_SWTMR_ID_INVALID; } intSave = LOS_IntLock(); swtmrCbId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT; swtmr = g_swtmrCBArray + swtmrCbId; ⑵ if (swtmr->usTimerID != swtmrId) { LOS_IntRestore(intSave); return LOS_ERRNO_SWTMR_ID_INVALID; } ⑶ switch (swtmr->ucState) { 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; } LOS_IntRestore(intSave); return ret; }
接下來,咱們繼續看看如何調用函數OsSwtmrStop(swtmr)中止定時器。函數特別簡單,⑴處從排序鏈表中刪除該定時器的排序鏈表節點,更改定時器的狀態。⑵若是已使能任務調度,則修改過時時間。
LITE_OS_SEC_TEXT VOID OsSwtmrStop(SWTMR_CTRL_S *swtmr) { ⑴ OsDeleteSortLink(&swtmr->stSortList, OS_SORT_LINK_SWTMR); swtmr->ucState = OS_SWTMR_STATUS_CREATED; if (LOS_TaskIsRunning()) { ⑵ OsSchedUpdateExpireTime(OsGetCurrSchedTimeCycle()); #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT].isAligned = 0; #endif } }
定時器加入到超時排序鏈表後,隨時時間一個tick一個tick的逝去,須要不斷的檢查定時器是否超時到期。從以前的文章,已經知道系統每走過一個tick,系統就會調用一次Tick中斷的處理函數OsTickHandler(),該函數會調用定時器掃描函數OsSwtmrScan()來掃描、更新定時器時間。咱們看下OsSwtmrScan()的代碼。
⑴處獲取超時排序鏈表的鏈表節點listObject,⑵判斷排序鏈表是否爲空,爲空則返回。⑶獲取排序鏈表的下一個鏈表節點sortList。⑷循環遍歷超時排序鏈表上響應時間小於等於當前時間的鏈表節點,意味着定時器到期,須要處理定時器的回調函數。⑸從超時排序鏈表中刪除超時的節點,⑹獲取定時器控制塊SWTMR_CTRL_S *swtmr,調用函數OsSwtmrTimeoutHandle(swtmr)執行定時器回調函數,並設置須要調度的標記needSchedule。⑺若是超時排序鏈表爲空則終止循環。
STATIC BOOL OsSwtmrScan(VOID) { BOOL needSchedule = FALSE; ⑴ LOS_DL_LIST *listObject = &g_swtmrSortLinkList->sortLink; ⑵ if (LOS_ListEmpty(listObject)) { return needSchedule; } ⑶ SortLinkList *sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); UINT64 currTime = OsGetCurrSchedTimeCycle(); ⑷ while (sortList->responseTime <= currTime) { ⑸ OsDeleteNodeSortLink(g_swtmrSortLinkList, sortList); ⑹ SWTMR_CTRL_S *swtmr = LOS_DL_LIST_ENTRY(sortList, SWTMR_CTRL_S, stSortList); OsSwtmrTimeoutHandle(swtmr); needSchedule = TRUE; ⑺ if (LOS_ListEmpty(listObject)) { break; } sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); } return needSchedule; }
咱們最後看下函數OsSwtmrTimeoutHandle()。⑴處把定時器回調函數寫入定時器隊列。⑵若是是一次性定時器,會把這個定時器刪除,回收到空閒定時器鏈表,狀態設置爲未使用狀態,而後更新定時器的編號timerId。⑶若是定時器屬於週期性定時器,從新啓動定時器。⑷若是是一次性定時器但不刪除,則把定時器狀態設置爲建立狀態。
STATIC VOID OsSwtmrTimeoutHandle(SWTMR_CTRL_S *swtmr) { SwtmrHandlerItem swtmrHandler; swtmrHandler.handler = swtmr->pfnHandler; swtmrHandler.arg = swtmr->uwArg; ⑴ (VOID)LOS_QueueWriteCopy(g_swtmrHandlerQueue, &swtmrHandler, sizeof(SwtmrHandlerItem), LOS_NO_WAIT); ⑵ if (swtmr->ucMode == LOS_SWTMR_MODE_ONCE) { OsSwtmrDelete(swtmr); if (swtmr->usTimerID < (OS_SWTMR_MAX_TIMERID - LOSCFG_BASE_CORE_SWTMR_LIMIT)) { swtmr->usTimerID += LOSCFG_BASE_CORE_SWTMR_LIMIT; } else { swtmr->usTimerID %= LOSCFG_BASE_CORE_SWTMR_LIMIT; } ⑶ } else if (swtmr->ucMode == LOS_SWTMR_MODE_PERIOD) { OsSwtmrStart(swtmr); ⑷ } else if (swtmr->ucMode == LOS_SWTMR_MODE_NO_SELFDELETE) { swtmr->ucState = OS_SWTMR_STATUS_CREATED; } }
本文帶領你們一塊兒剖析了鴻蒙輕內核的定時器模塊的源代碼,包含定時器的結構體、定時器池初始化、定時器建立、刪除、啓動中止等。感謝閱讀,若有任何問題、建議,均可以留言給咱們: https://gitee.com/openharmony/kernel_liteos_m/issues 。爲了更容易找到鴻蒙輕內核代碼倉,建議訪問 https://gitee.com/openharmony/kernel_liteos_m ,關注Watch
、點贊Star
、並Fork
到本身帳戶下,謝謝。