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