天天看點

鴻蒙輕核心定時器Swtmr:不受硬體和數量限制,滿足使用者需求

摘要:本文通過分析鴻蒙輕核心定時器子產品的源碼,掌握定時器使用上的差異。

本文分享自華為雲社群《鴻蒙輕核心M核源碼分析系列十四 軟體定時器Swtmr》,作者:zhushy 。

軟體定時器(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。其他結構體成員的解釋見注釋部分。

另外,還對回調函數及其參數單獨定義了一個結構體SwtmrHandlerItem,如下:

定時器頭檔案kernel\include\los_swtmr.h中還提供了相關的枚舉和宏,從定時器池裡擷取定時器控制塊的宏定義OS_SWT_FROM_SID如下:

定時器在核心中預設開啟,使用者可以通過宏LOSCFG_BASE_CORE_SWTMR進行關閉。開啟定時器的情況下,在系統啟動時,在kernel\src\los_init.c中調用OsSwtmrInit()進行定時器子產品初始化。下面,我們分析下定時器初始化的代碼。

⑴處如果開啟定時器對齊宏LOSCFG_BASE_CORE_SWTMR_ALIGN,清零g_swtmrAlignID數組。定時器的數量由宏LOSCFG_BASE_CORE_SWTMR_LIMIT定義,⑵處計算定時器池需要的記憶體大小,然後為定時器申請記憶體,如果申請失敗,則傳回錯誤。⑶初始化空閑定時器連結清單g_swtmrFreeList,維護未使用的定時器。循環每一個定時器進行初始化,為每一個定時器節點指定索引timerId,定時器控制塊依次指向下一個定時器控制塊。

⑷處代碼為定時器建立隊列,隊列的消息大小OS_SWTMR_HANDLE_QUEUE_SIZE等于定時器的數量LOSCFG_BASE_CORE_SWTMR_LIMIT,消息内容的最大大小sizeof(SwtmrHandlerItem)。後文分析定時器隊列讀取寫入消息的時候具體來看是什麼消息。⑸處調用函數OsSwtmrTaskCreate()建立定時器任務,定時器任務優先級最高,任務的入口函數為OsSwtmrTask(),後文會分析該函數。⑹處初始化定時器排序連結清單,源碼分析系列之前的文章分析過,可以閱讀下排序連結清單資料結構章節。⑺處注冊定時器掃描函數OsSwtmrScan。

我們再看一下定時器任務的入口函數為OsSwtmrTask()。⑴進行for永久循環,隊列讀取不到資料時會阻塞,因為優先級比較高,定時器隊列有資料時該任務就會執行。從定時器隊列中讀取定時器處理函數位址放入指針位址&swtmrHandle,讀取的長度為sizeof(SwtmrHandlerItem)。成功讀取後,擷取定時器回調函數及其參數,然後⑵處執行定時器回調函數。記錄定時器回調函數的執行時間,⑶處判斷執行時間是否逾時,如果逾時,列印警告資訊。

我們分析下建立定時器函數LOS_SwtmrCreate()的代碼。先不考慮定時器對齊LOSCFG_BASE_CORE_SWTMR_ALIGN的情況。先看下函數參數,interval是定時器執行時間間隔,mode是建立的定時器模式,handler、arg是定時器回調函數及其參數。swtmrId是定時器編号。

⑴處對傳入參數定時器逾時間隔、定時器模式、回調函數,定時器編号進行校驗。⑵判斷空閑定時器池是否為空,為空則傳回錯誤,無法建立定時器。⑶處如果定時器不為空,則擷取定時器控制塊swtmr。⑷處對定時器控制塊資訊進行初始化。⑸處把該定時器排序連結清單節點的響應時間responseTime初始化為-1。

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

⑴處判斷定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,如果超過則傳回錯誤碼。如果定時器編号沒有問題,擷取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要删除的定時器swtmrId是否比對,不比對則傳回錯誤碼。⑶處判斷定時器的狀态,如果定時器定時器沒有建立,不能删除。如果定時器計時中,需要先停止OsSwtmrStop(swtmr),然後再删除OsSwtmrDelete(swtmr)。

接下來,我們繼續看看如何調用函數OsSwtmrDelete(swtmr)删除定時器。函數特别簡單,把定時器放入空閑定時器連結清單g_swtmrFreeList頭部,然後把定時器狀态改為未使用狀态就完成了删除。

建立完畢定時器後,我們可以使用函數LOS_SwtmrStart(UINT32 swtmrId)來啟動定時器,下面通過分析源碼看看如何啟動定時器的。

⑴處判斷定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,如果超過則傳回錯誤碼。如果定時器編号沒有問題,擷取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要啟動的定時器swtmrId是否比對,不比對則傳回錯誤碼。⑶處判斷定時器的狀态,如果定時器定時器沒有建立,不能啟動。如果定時器計時中,需要先停止OsSwtmrStop(swtmr),然後再啟動OsSwtmrStart(swtmr)。

接下來,我們繼續看看如何調用函數OsSwtmrStart(swtmr)啟動定時器。函數特别簡單,⑴設定定時器的等待逾時時間,并把定時器狀态改為計時中。⑵處把該定時器插入逾時排序連結清單中。如果已使能任務排程,則修改過期時間。

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

⑴處判斷定時器swtmrId是否超過OS_SWTMR_MAX_TIMERID,如果超過則傳回錯誤碼。如果定時器編号沒有問題,擷取定時器控制塊LosSwtmrCB *swtmr。⑵處判斷要啟動的定時器swtmrId是否比對,不比對則傳回錯誤碼。⑶處判斷定時器的狀态,如果定時器定時器沒有建立,沒有啟動,不能停止。如果定時器計時中,會繼續調用OsSwtmrStop(swtmr)停止定時器。

接下來,我們繼續看看如何調用函數OsSwtmrStop(swtmr)停止定時器。函數特别簡單,⑴處從排序連結清單中删除該定時器的排序連結清單節點,更改定時器的狀态。⑵如果已使能任務排程,則修改過期時間。

定時器加入到逾時排序連結清單後,随時時間一個tick一個tick的逝去,需要不斷的檢查定時器是否逾時到期。從之前的文章,已經知道系統每走過一個tick,系統就會調用一次Tick中斷的處理函數OsTickHandler(),該函數會調用定時器掃描函數OsSwtmrScan()來掃描、更新定時器時間。我們看下OsSwtmrScan()的代碼。

⑴處擷取逾時排序連結清單的連結清單節點listObject,⑵判斷排序連結清單是否為空,為空則傳回。⑶擷取排序連結清單的下一個連結清單節點sortList。⑷循環周遊逾時排序連結清單上響應時間小于等于目前時間的連結清單節點,意味着定時器到期,需要處理定時器的回調函數。⑸從逾時排序連結清單中删除逾時的節點,⑹擷取定時器控制塊SWTMR_CTRL_S *swtmr,調用函數OsSwtmrTimeoutHandle(swtmr)執行定時器回調函數,并設定需要排程的标記needSchedule。⑺如果逾時排序連結清單為空則終止循環。

我們最後看下函數OsSwtmrTimeoutHandle()。⑴處把定時器回調函數寫入定時器隊列。⑵如果是一次性定時器,會把這個定時器删除,回收到空閑定時器連結清單,狀态設定為未使用狀态,然後更新定時器的編号timerId。⑶如果定時器屬于周期性定時器,重新啟動定時器。⑷如果是一次性定時器但不删除,則把定時器狀态設定為建立狀态。

本文帶領大家一起剖析了鴻蒙輕核心的定時器子產品的源代碼,包含定時器的結構體、定時器池初始化、定時器建立、删除、啟動停止等。感謝閱讀,如有任何問題、建議,都可以留言給我們: https://gitee.com/openharmony/kernel_liteos_m/issues 。為了更容易找到鴻蒙輕核心代碼倉,建議通路 https://gitee.com/openharmony/kernel_liteos_m ,關注<code>Watch</code>、點贊<code>Star</code>、并<code>Fork</code>到自己賬戶下,謝謝。

點選關注,第一時間了解華為雲新鮮技術~

繼續閱讀