鴻蒙輕內(nèi)核M核源碼分析系列十四 軟件定時器Swtmr
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
軟件定時器(Software Timer)是基于系統(tǒng)Tick時鐘中斷且由軟件來模擬的定時器。當(dāng)經(jīng)過設(shè)定的Tick數(shù)后,會觸發(fā)用戶自定義的回調(diào)函數(shù)。硬件定時器受硬件的限制,數(shù)量上不足以滿足用戶的實際需求。鴻蒙輕內(nèi)核提供了軟件定時器功能可以提供更多的定時器,滿足用戶需求。
本文通過分析鴻蒙輕內(nèi)核定時器模塊的源碼,掌握定時器使用上的差異。
接下來,我們看下定時器的結(jié)構(gòu)體,定時器初始化,定時器常用操作的源代碼。
1、定時器結(jié)構(gòu)體定義和常用宏定義
1.1 定時器結(jié)構(gòu)體定義
在文件kernel\include\los_swtmr.h定義的定時器控制塊結(jié)構(gòu)體為SWTMR_CTRL_S,結(jié)構(gòu)體源代碼如下。定時器狀態(tài).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。其他結(jié)構(gòu)體成員的解釋見注釋部分。
- typedef struct tagSwTmrCtrl {
- struct tagSwTmrCtrl *pstNext; /* 指向下一個定時器結(jié)構(gòu)體的指針 */
- UINT8 ucState; /* 定時器狀態(tài),取值枚舉SwtmrState */
- UINT8 ucMode; /* 定時器模式,取值枚舉enSwTmrType */
- #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
- UINT8 ucRouses; /* 喚醒開關(guān) */
- UINT8 ucSensitive; /* 對齊開關(guān) */
- #endif
- UINT32 usTimerID; /* 定時器編號Id */
- UINT32 uwCount; /* 定時器運(yùn)行的次數(shù) */
- UINT32 uwInterval; /* 周期定時器超時間隔 (單位: tick) */
- UINT32 uwArg; /* 定時器超時回調(diào)函數(shù)參數(shù) */
- SWTMR_PROC_FUNC pfnHandler; /* 定時器超時回調(diào)函數(shù) */
- SortLinkList stSortList; /* 定時器排序鏈表 */
- } SWTMR_CTRL_S;
另外,還對回調(diào)函數(shù)及其參數(shù)單獨(dú)定義了一個結(jié)構(gòu)體SwtmrHandlerItem,如下:
- typedef struct {
- SWTMR_PROC_FUNC handler; /**< 定時器超時回調(diào)函數(shù) */
- UINTPTR arg; /**< 定時器超時回調(diào)函數(shù)參數(shù) */
- } SwtmrHandlerItem;
1.2 定時器常用宏定義
定時器頭文件kernel\include\los_swtmr.h中還提供了相關(guān)的枚舉和宏,從定時器池里獲取定時器控制塊的宏定義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, /**< 定時器已創(chuàng)建 */
- OS_SWTMR_STATUS_TICKING /**< 定時器計時中 */
- };
- #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
- enum enSwTmrRousesType {
- OS_SWTMR_ROUSES_IGNORE, /* 定時器不能喚醒系統(tǒng) */
- OS_SWTMR_ROUSES_ALLOW, /* 定時器能喚醒系統(tǒng) */
- };
- 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 */
- };
2、定時器初始化
定時器在內(nèi)核中默認(rèn)開啟,用戶可以通過宏LOSCFG_BASE_CORE_SWTMR進(jìn)行關(guān)閉。開啟定時器的情況下,在系統(tǒng)啟動時,在kernel\src\los_init.c中調(diào)用OsSwtmrInit()進(jìn)行定時器模塊初始化。下面,我們分析下定時器初始化的代碼。
⑴處如果開啟定時器對齊宏LOSCFG_BASE_CORE_SWTMR_ALIGN,清零g_swtmrAlignID數(shù)組。定時器的數(shù)量由宏LOSCFG_BASE_CORE_SWTMR_LIMIT定義。
⑵處計算定時器池需要的內(nèi)存大小,然后為定時器申請內(nèi)存,如果申請失敗,則返回錯誤。
⑶初始化空閑定時器鏈表g_swtmrFreeList,維護(hù)未使用的定時器。循環(huán)每一個定時器進(jìn)行初始化,為每一個定時器節(jié)點指定索引timerId,定時器控制塊依次指向下一個定時器控制塊。
⑷處代碼為定時器創(chuàng)建隊列,隊列的消息大小OS_SWTMR_HANDLE_QUEUE_SIZE等于定時器的數(shù)量LOSCFG_BASE_CORE_SWTMR_LIMIT,消息內(nèi)容的最大大小sizeof(SwtmrHandlerItem)。后文分析定時器隊列讀取寫入消息的時候具體來看是什么消息。
⑸處調(diào)用函數(shù)OsSwtmrTaskCreate()創(chuàng)建定時器任務(wù),定時器任務(wù)優(yōu)先級最高,任務(wù)的入口函數(shù)為OsSwtmrTask(),后文會分析該函數(shù)。
⑹處初始化定時器排序鏈表,源碼分析系列之前的文章分析過,可以閱讀下排序鏈表數(shù)據(jù)結(jié)構(gòu)章節(jié)。
⑺處注冊定時器掃描函數(shù)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;
- }
我們再看一下定時器任務(wù)的入口函數(shù)為OsSwtmrTask()。⑴進(jìn)行for永久循環(huán),隊列讀取不到數(shù)據(jù)時會阻塞,因為優(yōu)先級比較高,定時器隊列有數(shù)據(jù)時該任務(wù)就會執(zhí)行。從定時器隊列中讀取定時器處理函數(shù)地址放入指針地址&swtmrHandle,讀取的長度為sizeof(SwtmrHandlerItem)。成功讀取后,獲取定時器回調(diào)函數(shù)及其參數(shù),然后⑵處執(zhí)行定時器回調(diào)函數(shù)。記錄定時器回調(diào)函數(shù)的執(zhí)行時間,⑶處判斷執(zhí)行時間是否超時,如果超時,打印警告信息。
- 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));
- }
- }
- }
- }
3、定時器常用操作
3.1 定時器創(chuàng)建
我們分析下創(chuàng)建定時器函數(shù)LOS_SwtmrCreate()的代碼。先不考慮定時器對齊LOSCFG_BASE_CORE_SWTMR_ALIGN的情況。先看下函數(shù)參數(shù),interval是定時器執(zhí)行時間間隔,mode是創(chuàng)建的定時器模式,handler、arg是定時器回調(diào)函數(shù)及其參數(shù)。swtmrId是定時器編號。
⑴處對傳入?yún)?shù)定時器超時間隔、定時器模式、回調(diào)函數(shù),定時器編號進(jìn)行校驗。⑵判斷空閑定時器池是否為空,為空則返回錯誤,無法創(chuàng)建定時器。⑶處如果定時器不為空,則獲取定時器控制塊swtmr。⑷處對定時器控制塊信息進(jìn)行初始化。⑸處把該定時器排序鏈表節(jié)點的響應(yīng)時間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;
- }
3.2 定時器刪除
我們可以使用函數(shù)LOS_SwtmrDelete(UINT32 swtmrId)來刪除定時器,下面通過分析源碼看看如何刪除定時器的。
⑴處判斷定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,如果超過則返回錯誤碼。如果定時器編號沒有問題,獲取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要刪除的定時器swtmrId是否匹配,不匹配則返回錯誤碼。⑶處判斷定時器的狀態(tài),如果定時器定時器沒有創(chuàng)建,不能刪除。如果定時器計時中,需要先停止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;
- }
接下來,我們繼續(xù)看看如何調(diào)用函數(shù)OsSwtmrDelete(swtmr)刪除定時器。函數(shù)特別簡單,把定時器放入空閑定時器鏈表g_swtmrFreeList頭部,然后把定時器狀態(tài)改為未使用狀態(tài)就完成了刪除。
- 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
- }
3.3 定時器啟動
創(chuàng)建完畢定時器后,我們可以使用函數(shù)LOS_SwtmrStart(UINT32 swtmrId)來啟動定時器,下面通過分析源碼看看如何啟動定時器的。
⑴處判斷定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,如果超過則返回錯誤碼。如果定時器編號沒有問題,獲取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要啟動的定時器swtmrId是否匹配,不匹配則返回錯誤碼。⑶處判斷定時器的狀態(tài),如果定時器定時器沒有創(chuàng)建,不能啟動。如果定時器計時中,需要先停止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;
- }
接下來,我們繼續(xù)看看如何調(diào)用函數(shù)OsSwtmrStart(swtmr)啟動定時器。函數(shù)特別簡單,⑴設(shè)置定時器的等待超時時間,并把定時器狀態(tài)改為計時中。⑵處把該定時器插入超時排序鏈表中。如果已使能任務(wù)調(diào)度,則修改過期時間。
- 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);
- }
- }
3.4 定時器停止
我們可以使用函數(shù)LOS_SwtmrStop(UINT32 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(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;
- }
接下來,我們繼續(xù)看看如何調(diào)用函數(shù)OsSwtmrStop(swtmr)停止定時器。函數(shù)特別簡單,⑴處從排序鏈表中刪除該定時器的排序鏈表節(jié)點,更改定時器的狀態(tài)。⑵如果已使能任務(wù)調(diào)度,則修改過期時間。
- 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
- }
- }
4、定時器和Tick時間關(guān)系
定時器加入到超時排序鏈表后,隨時時間一個tick一個tick的逝去,需要不斷的檢查定時器是否超時到期。從之前的文章,已經(jīng)知道系統(tǒng)每走過一個tick,系統(tǒng)就會調(diào)用一次Tick中斷的處理函數(shù)OsTickHandler(),該函數(shù)會調(diào)用定時器掃描函數(shù)OsSwtmrScan()來掃描、更新定時器時間。我們看下OsSwtmrScan()的代碼。
⑴處獲取超時排序鏈表的鏈表節(jié)點listObject,⑵判斷排序鏈表是否為空,為空則返回。⑶獲取排序鏈表的下一個鏈表節(jié)點sortList。⑷循環(huán)遍歷超時排序鏈表上響應(yīng)時間小于等于當(dāng)前時間的鏈表節(jié)點,意味著定時器到期,需要處理定時器的回調(diào)函數(shù)。⑸從超時排序鏈表中刪除超時的節(jié)點,⑹獲取定時器控制塊SWTMR_CTRL_S *swtmr,調(diào)用函數(shù)OsSwtmrTimeoutHandle(swtmr)執(zhí)行定時器回調(diào)函數(shù),并設(shè)置需要調(diào)度的標(biāo)記needSchedule。⑺如果超時排序鏈表為空則終止循環(huán)。
- 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;
- }
我們最后看下函數(shù)OsSwtmrTimeoutHandle()。⑴處把定時器回調(diào)函數(shù)寫入定時器隊列。⑵如果是一次性定時器,會把這個定時器刪除,回收到空閑定時器鏈表,狀態(tài)設(shè)置為未使用狀態(tài),然后更新定時器的編號timerId。⑶如果定時器屬于周期性定時器,重新啟動定時器。⑷如果是一次性定時器但不刪除,則把定時器狀態(tài)設(shè)置為創(chuàng)建狀態(tài)。
- 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;
- }
- }
小結(jié)
本文帶領(lǐng)大家一起剖析了鴻蒙輕內(nèi)核的定時器模塊的源代碼,包含定時器的結(jié)構(gòu)體、定時器池初始化、定時器創(chuàng)建、刪除、啟動停止等。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)