LiteOS內核源碼分析系列四 LiteOS內核時間管理

      網友投稿 952 2025-03-31

      Huawei LiteOS的時間管理模塊以系統時鐘為基礎,可以分為2部分,一部分是SysTick中斷,為任務調度提供必要的時鐘節拍;另外一部分是,給應用程序提供所有和時間有關的服務,如時間轉換、統計、延遲功能。

      系統時鐘是由定時器/計數器產生的輸出脈沖觸發中斷產生的,一般定義為整數或長整數。輸出脈沖的周期叫做一個“時鐘滴答”,也稱為時標或者Tick。Tick是操作系統的基本時間單位,由用戶配置的每秒Tick數決定。如果用戶配置每秒的Tick數目為1000,則1個Tick等于1ms的時長。另外一個計時單位是Cycle,這是系統最小的計時單位。Cycle的時長由系統主時鐘頻率決定,系統主時鐘頻率就是每秒鐘的Cycle數,對于216 MHz的CPU,1秒產生216000000個cycles。

      用戶以秒、毫秒為單位計時,而操作系統以Tick為單位計時,當用戶需要對系統進行操作時,例如任務掛起、延時等,此時可以使用時間管理模塊對Tick和秒/毫秒進行轉換。

      文中所涉及的源代碼,均可以在LiteOS開源站點https://gitee.com/LiteOS/LiteOS 獲取。位操作模塊源代碼、開發文檔如下:

      內核時間管理源代碼

      時間管理模塊源文件,包括頭文件kernel\include\los_tick.h、私有頭文件[kernel\base\include\los_tick_pri.h](https://gitee.com/LiteOS/LiteOS/blob/master/kernel/base/include/los_tick_pri.h、C源代碼文件kernel\base\los_tick.c。

      開發指南時間管理模塊文檔

      在線文檔https://gitee.com/LiteOS/LiteOS/blob/feature/doc/Huawei_LiteOS_Kernel_Developer_Guide_zh.md#%E6%97%B6%E9%97%B4%E7%AE%A1%E7%90%86。

      下面,我們剖析下時間管理模塊的源代碼,以LiteOS開源工程支持的板子之一STM32F769IDiscovery為例進行源碼分析。

      1、時間管理初始化和啟動。

      我們先看下時間管理模塊的相關配置,然后再剖析如何初始化,如何啟動。

      1.1 時間管理相關的配置

      時間管理模塊依賴系統時鐘OS_SYS_CLOCK和每秒Tick數目LOSCFG_BASE_CORE_TICK_PER_SECOND兩個配置選項。在系統啟動時,targets\STM32F769IDISCOVERY\Src\main.c的main()函數調用targets\STM32F769IDISCOVERY\Src\platform_init.c文件中的void HardwareInit(void)進行硬件初始化,初始化時會調用void SystemClock_Config(void)進行系統時鐘的配置。完成系統時鐘的配置后,SystemCoreClock賦值為216000000Hz。通過下面兩個宏定義,OS_SYS_CLOCK也表示系統時鐘。

      文件kernel\include\los_config.h:

      /** * @ingroup los_config * System clock (unit: HZ) */ #ifndef OS_SYS_CLOCK #define OS_SYS_CLOCK (get_bus_clk()) #endif

      文件targets\STM32F769IDISCOVERY\include\hisoc\clock.h:

      #define get_bus_clk() SystemCoreClock // default: 216000000

      另外一個配置項,每秒Tick數目LOSCFG_BASE_CORE_TICK_PER_SECOND,用戶可以通過LiteOS提供的組件配置工具menuconfig進行設置,配置路徑在Kernel → Basic Config → Task → Tick Value Per Second,支持的開發板也提供了默認值。

      1.2 時間管理初始化OsTickInit()

      在系統啟動時,在kernel\init\los_init.c中調用VOID OsRegister(VOID)設置系統時鐘、Tick配置。⑴處全局變量g_tickPerSecond賦值為LOSCFG_BASE_CORE_TICK_PER_SECOND,也表示每秒配置多少個Tick。⑵處的宏定義把OS_SYS_CLOCK賦值給g_sysClock,都表示系統時鐘。后文的代碼解析會涉及這些變量的使用。

      LITE_OS_SEC_TEXT_INIT static VOID OsRegister(VOID) { #ifdef LOSCFG_LIB_CONFIGURABLE g_osSysClock = OS_SYS_CLOCK_CONFIG; g_tickPerSecond = LOSCFG_BASE_CORE_TICK_PER_SECOND_CONFIG; g_taskLimit = LOSCFG_BASE_CORE_TSK_LIMIT_CONFIG; g_taskMaxNum = g_taskLimit + 1; g_taskMinStkSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE_CONFIG; g_taskIdleStkSize = LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE_CONFIG; g_taskDfltStkSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE_CONFIG; g_taskSwtmrStkSize = LOSCFG_BASE_CORE_TSK_SWTMR_STACK_SIZE_CONFIG; g_swtmrLimit = LOSCFG_BASE_CORE_SWTMR_LIMIT_CONFIG; g_semLimit = LOSCFG_BASE_IPC_SEM_LIMIT_CONFIG; g_muxLimit = LOSCFG_BASE_IPC_MUX_LIMIT_CONFIG; g_queueLimit = LOSCFG_BASE_IPC_QUEUE_LIMIT_CONFIG; g_timeSliceTimeOut = LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT_CONFIG; #else ⑴ g_tickPerSecond = LOSCFG_BASE_CORE_TICK_PER_SECOND; #endif ⑵ SET_SYS_CLOCK(OS_SYS_CLOCK); #ifdef LOSCFG_KERNEL_NX LOS_SET_NX_CFG(true); #else LOS_SET_NX_CFG(false); #endif LOS_SET_DL_NX_HEAP_BASE(LOS_DL_HEAP_BASE); LOS_SET_DL_NX_HEAP_SIZE(LOS_DL_HEAP_SIZE); return; }

      在kernel\init\los_init.c中會繼續調用UINT32 OsTickInit(UINT32 systemClock, UINT32 tickPerSecond)來初始化時間配置。該函數需要2個參數,分別是上文配置的系統時鐘和每秒的tick數。進一步調用HalClockInit()函數。

      LITE_OS_SEC_TEXT_INIT UINT32 OsTickInit(UINT32 systemClock, UINT32 tickPerSecond) { if ((systemClock == 0) || (tickPerSecond == 0) || (tickPerSecond > systemClock)) { return LOS_ERRNO_TICK_CFG_INVALID; } HalClockInit(); return LOS_OK; }

      HalClockInit()函數定義在targets\bsp\hw\arm\timer\arm_cortex_m\systick.c,使用LOS_HwiCreate()為中斷號M_INT_NUM創建一個中斷,每一個Tick中斷發生時,都會調用中斷處理程序OsTickHandler(),這個函數后文會分析。

      #define M_INT_NUM 15 VOID HalClockInit(VOID) { UINT32 ret = LOS_HwiCreate(M_INT_NUM, 0, 0, OsTickHandler, 0); if (ret != 0) { PRINTK("ret of LOS_HwiCreate = %#x\n", ret); } #if defined (LOSCFG_ARCH_ARM_CORTEX_M) && (LOSCFG_KERNEL_CPUP) TimerHwiCreate(); #endif }

      1.3 時間管理模塊啟動OsTickStart()

      在系統開始調度之前,函數INT32 main(VOID)會調用系統啟動函數VOID OsStart(VOID),它會調用時間模塊啟動函數OsTickStart(),進一步調用HalClockStart()。我們分析下函數的代碼實現。

      ⑴處全局變量g_cyclesPerTick表示每Tick對應的cycle數目。⑵處函數定義在arch\arm\cortex_m\cmsis\core_cm7.h文件中,初始化系統定時器Systick并啟動,Systick相關的代碼自行閱讀。⑶處調用LOS_HwiEnable()函數使能Tick中斷。

      文件kernel\base\los_tick.c:

      LITE_OS_SEC_TEXT_INIT VOID OsTickStart(VOID) { HalClockStart(); }

      文件targets\bsp\hw\arm\timer\arm_cortex_m\systick.c:

      VOID HalClockStart(VOID) { if ((OS_SYS_CLOCK == 0) || (LOSCFG_BASE_CORE_TICK_PER_SECOND == 0) || (LOSCFG_BASE_CORE_TICK_PER_SECOND > OS_SYS_CLOCK)) { return; } ⑴ g_cyclesPerTick = OS_CYCLE_PER_TICK; ⑵ (VOID)SysTick_Config(OS_CYCLE_PER_TICK); ⑶ UINT32 ret = LOS_HwiEnable(M_INT_NUM); if (ret != 0) { PRINTK("LOS_HwiEnable failed. ret = %#x\n", ret); } }

      1.4 Tick中斷處理函數OsTickHandler()

      這是時間管理模塊中執行最頻繁的函數VOID OsTickHandler(VOID),每當Tick中斷發生時就會調用該函數。⑴處會更新全局數組全局數組g_tickCount每個核的tick數據。⑵和tickless特性相關,后續系列分析。⑶處會遍歷任務的排序鏈表,檢查是否有超時的任務。⑷處如果支持定時器特性,會檢查定時器排序鏈表中的定時器是否超時。

      LITE_OS_SEC_TEXT VOID OsTickHandler(VOID) { UINT32 intSave; TICK_LOCK(intSave); ⑴ g_tickCount[ArchCurrCpuid()]++; TICK_UNLOCK(intSave); #ifdef LOSCFG_KERNEL_TICKLESS ⑵ OsTickIrqFlagSet(OsTicklessFlagGet()); #endif #if (LOSCFG_BASE_CORE_TICK_HW_TIME == YES) HalClockIrqClear(); /* diff from every platform */ #endif #ifdef LOSCFG_BASE_CORE_TIMESLICE OsTimesliceCheck(); #endif ⑶ OsTaskScan(); /* task timeout scan */ #if (LOSCFG_BASE_CORE_SWTMR == YES) ⑷ OsSwtmrScan(); #endif }

      2、LiteOS內核時間管理常用操作

      Huawei LiteOS的時間管理提供下面幾種功能,時間轉換、時間統計、延時管理等,我們剖析下這些接口的源代碼實現。

      2.1 時間轉換操作

      2.1.1 毫秒轉換成Tick

      函數UINT32 LOS_MS2Tick(UINT32 millisec)把輸入參數毫秒數UINT32 millisec可以轉化為Tick數目。代碼中OS_SYS_MS_PER_SECOND,即1秒等于1000毫秒。時間轉換也比較簡單,知道一秒多少Tick,除以OS_SYS_MS_PER_SECOND,得出1毫秒多少Tick,然后乘以millisec,計算出結果值。

      LITE_OS_SEC_TEXT_MINOR UINT32 LOS_MS2Tick(UINT32 millisec) { if (millisec == UINT32_MAX) { return UINT32_MAX; } return (UINT32)(((UINT64)millisec * LOSCFG_BASE_CORE_TICK_PER_SECOND) / OS_SYS_MS_PER_SECOND); }

      2.1.2 Tick轉化為毫秒

      LiteOS內核源碼分析系列四 LiteOS內核時間管理

      函數UINT32 LOS_Tick2MS(UINT32 tick)把輸入參數Tick數目轉換為毫秒數。時間轉換也比較簡單,tick除以LOSCFG_BASE_CORE_TICK_PER_SECOND,計算出多少秒,然后轉換成毫秒,計算出結果值。

      LITE_OS_SEC_TEXT_MINOR UINT32 LOS_Tick2MS(UINT32 tick) { return (UINT32)(((UINT64)tick * OS_SYS_MS_PER_SECOND) / LOSCFG_BASE_CORE_TICK_PER_SECOND); }

      2.2 時間統計操作

      2.2.1 每個Tick多少Cycle數

      函數UINT32 LOS_CyclePerTickGet(VOID)計算1個tick等于多少cycle。g_sysClock系統時鐘表示1秒多少cycle,LOSCFG_BASE_CORE_TICK_PER_SECOND一秒多少tick,相除計算出1 tick多少cycle數。

      LITE_OS_SEC_TEXT_MINOR UINT32 LOS_CyclePerTickGet(VOID) { return g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND; }

      2.2.2 獲取自系統啟動以來的Tick數

      UINT64 LOS_TickCountGet(VOID)函數計算自系統啟動以來的Tick數。需要注意,在關中斷的情況下不進行計數,不能作為準確時間使用。全局數組UINT64 g_tickCount[LOSCFG_KERNEL_CORE_NUM]記錄每一個核的自系統啟動以來的Tick數,每次Tick中斷發生時,在函數VOID OsTickHandler(VOID)中會更新這個數組的數據。我們取第一個核的Tick數作為返回結果。

      LITE_OS_SEC_TEXT_MINOR UINT64 LOS_TickCountGet(VOID) { UINT32 intSave; UINT64 tick; TICK_LOCK(intSave); tick = g_tickCount[0]; TICK_UNLOCK(intSave); return tick; }

      2.2.3 獲取自系統啟動以來的Cycle數

      VOID LOS_GetCpuCycle(UINT32 *highCnt, UINT32 *lowCnt)函數獲取自系統啟動以來的Cycle數。這個函數調用定義在文件targets\bsp\hw\arm\timer\arm_cortex_m\systick.c中的HalClockGetCycles()函數獲取64位的無符號整數。返回結果按高低32位的無符號數值UINT32 *highCnt, UINT32 *lowCnt分別返回。

      LITE_OS_SEC_TEXT_MINOR VOID LOS_GetCpuCycle(UINT32 *highCnt, UINT32 *lowCnt) { UINT64 cycle; if ((highCnt == NULL) || (lowCnt == NULL)) { return; } cycle = HalClockGetCycles(); /* get the high 32 bits */ *highCnt = (UINT32)(cycle >> 32); /* get the low 32 bits */ *lowCnt = (UINT32)(cycle & 0xFFFFFFFFULL); }

      我們繼續看下函數HalClockGetCycles()函數。先關中斷,然后⑴處獲取啟動啟動以來的Tick數目。⑵處通過讀取當前值寄存器SysTick Current Value Register,獲取hwCycle。

      ⑷ cycle = (swTick * g_cyclesPerTick) + (g_cyclesPerTick - hwCycle);

      ⑶處表示中斷控制和狀態寄存器Interrupt Control and State Register的第TICK_INTR_CHECK位為1時,表示掛起systick中斷,tick沒有計數,需要加1校準。⑷處根據swTick、g_cyclesPerTick和hwCycle計算出自系統啟動以來的Cycle數。

      UINT64 HalClockGetCycles(VOID) { UINT64 swTick; UINT64 cycle; UINT32 hwCycle; UINT32 intSave; intSave = LOS_IntLock(); ⑴ swTick = LOS_TickCountGet(); ⑵ hwCycle = SysTick->VAL; ⑶ if ((SCB->ICSR & TICK_INTR_CHECK) != 0) { hwCycle = SysTick->VAL; swTick++; } ⑷ cycle = (swTick * g_cyclesPerTick) + (g_cyclesPerTick - hwCycle); LOS_IntRestore(intSave); #if defined (LOSCFG_ARCH_ARM_CORTEX_M) && (LOSCFG_KERNEL_CPUP) cycle = HalClockGetCpupCycles() * TIMER_CYCLE_SWITCH; #endif return cycle; }

      2.2.4 獲取自系統啟動以來的納秒數

      函數UINT64 LOS_CurrNanosec(VOID)計算獲取自系統啟動以來的納秒數。HalClockGetCycles()獲取自系統啟動以來的Cycle數,除以表示每秒多少cycle的系統時鐘g_sysClock,可以計算出自系統啟動以來的秒數,然后乘以秒和納秒的換算關系OS_SYS_NS_PER_SECOND,即可獲取自系統啟動以來的納秒數。代碼中出現2次除以OS_SYS_NS_PER_MS,來減小中間值避免數值溢出。

      LITE_OS_SEC_TEXT_MINOR UINT64 LOS_CurrNanosec(VOID) { UINT64 nanos; nanos = HalClockGetCycles() * (OS_SYS_NS_PER_SECOND / OS_SYS_NS_PER_MS) / (g_sysClock / OS_SYS_NS_PER_MS); return nanos; }

      2.3 延時管理

      2.3.1 LOS_Udelay()微秒等待

      以us為單位的忙等,但可以被優先級更高的任務搶占。該函數VOID LOS_Udelay(UINT32 usecs)進一步調用targets\bsp\hw\arm\timer\arm_cortex_m\systick.c文件中定義的函數VOID HalDelayUs(UINT32 usecs)。

      LITE_OS_SEC_TEXT_MINOR VOID LOS_Udelay(UINT32 usecs) { HalDelayUs(usecs); }

      繼續分析下函數VOID HalDelayUs(UINT32 usecs)。微秒轉換為納秒,計算當前的納秒數值,然后while循環,使用匯編指令空操作,等待超時。

      VOID HalDelayUs(UINT32 usecs) { UINT64 tmo = LOS_CurrNanosec() + usecs * OS_SYS_NS_PER_US; while (LOS_CurrNanosec() < tmo) { __asm__ volatile ("nop"); } }

      2.3.2 LOS_Mdelay()毫秒等待

      以ms為單位的忙等,但可以被優先級更高的任務搶占。該函數把參數UINT32 msecs毫秒轉換為微妙,需要考慮數值溢出的問題。

      LITE_OS_SEC_TEXT_MINOR VOID LOS_Mdelay(UINT32 msecs) { UINT32 delayUs = (UINT32_MAX / OS_SYS_US_PER_MS) * OS_SYS_US_PER_MS; while (msecs > UINT32_MAX / OS_SYS_US_PER_MS) { HalDelayUs(delayUs); msecs -= (UINT32_MAX / OS_SYS_US_PER_MS); } HalDelayUs(msecs * OS_SYS_US_PER_MS); }

      小結

      本文帶領大家一起剖析了LiteOS時間管理模塊的源代碼。時間管理模塊為任務調度提供必要的時鐘節拍,會向應用程序提供所有和時間有關的服務,如時間轉換、統計、延遲功能。

      感謝閱讀,如有任何問題、建議,都可以留言給我們: https://gitee.com/LiteOS/LiteOS/issues 。為了更容易找到LiteOS代碼倉,建議訪問 https://gitee.com/LiteOS/LiteOS ,關注Watch、Star、并Fork到自己賬戶下,如下圖,謝謝。

      輕量級操作系統 LiteOS

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

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

      上一篇:怎么導入大綱(ppt怎么導入大綱)
      下一篇:Excel表格怎么計算員工工資?
      相關文章
      亚洲AV无码成人精品区大在线| 亚洲av成人无码网站…| 国产亚洲男人的天堂在线观看| 亚洲第一区二区快射影院| 亚洲尹人香蕉网在线视颅| 亚洲国产精品特色大片观看完整版| 亚洲色偷偷狠狠综合网| 亚洲国产日韩成人综合天堂| 国产在亚洲线视频观看| 久久亚洲精品无码网站| 亚洲AⅤ男人的天堂在线观看| 亚洲成AV人片在WWW| 男人的天堂av亚洲一区2区| 亚洲AV成人精品日韩一区 | 久久人午夜亚洲精品无码区| 亚洲精品无码av中文字幕| 亚洲日韩国产AV无码无码精品| 亚洲情A成黄在线观看动漫软件| 亚洲av成人综合网| 国产亚洲精品成人AA片| 亚洲国产精品成人午夜在线观看| 亚洲精华国产精华精华液好用| 亚洲av无码专区在线电影| 国产精品亚洲а∨天堂2021 | 国内成人精品亚洲日本语音| 亚洲国产高清在线一区二区三区| 亚洲AV蜜桃永久无码精品| 亚洲精品无码专区久久同性男| 久久亚洲国产成人精品无码区| 亚洲午夜久久久久久久久电影网 | 亚洲日本成本人观看| 亚洲国产精品成人AV在线| 午夜亚洲av永久无码精品| 亚洲精品一级无码中文字幕| 中文字幕亚洲一区| 久热综合在线亚洲精品| 亚洲综合图片小说区热久久| 亚洲av日韩av综合| 亚洲av无码专区在线观看亚| 亚洲女人被黑人巨大进入| 日韩一卡2卡3卡4卡新区亚洲|