鴻蒙內(nèi)核源碼分析(時(shí)間管理篇) | Tick是操作系統(tǒng)的基本時(shí)間單位
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
本篇說清楚時(shí)間概念
讀本篇之前建議先讀鴻蒙內(nèi)核源碼分析(總目錄)其他篇.
時(shí)間概念太重要了,在鴻蒙內(nèi)核又是如何管理和使用時(shí)間的呢?
時(shí)間管理以系統(tǒng)時(shí)鐘 g_sysClock 為基礎(chǔ),給應(yīng)用程序提供所有和時(shí)間有關(guān)的服務(wù)。
● 用戶以秒、毫秒為單位計(jì)時(shí).
● 操作系統(tǒng)以Tick為單位計(jì)時(shí),這個(gè)認(rèn)識(shí)很重要. 每秒的tick大小很大程度上決定了內(nèi)核調(diào)度的次數(shù)多少.
● 當(dāng)用戶需要對(duì)系統(tǒng)進(jìn)行操作時(shí),例如任務(wù)掛起、延時(shí)等,此時(shí)需要時(shí)間管理模塊對(duì)Tick和秒/毫秒進(jìn)行轉(zhuǎn)換。
熟悉兩個(gè)概念:
● Cycle(周期):系統(tǒng)最小的計(jì)時(shí)單位。Cycle的時(shí)長由系統(tǒng)主時(shí)鐘頻率決定,系統(tǒng)主時(shí)鐘頻率就是每秒鐘的Cycle數(shù)。
● Tick(節(jié)拍):Tick是操作系統(tǒng)的基本時(shí)間單位,由用戶配置的每秒Tick數(shù)決定,可大可小.
怎么去理解他們之間的關(guān)系呢?看幾個(gè)宏定義就清楚了.
時(shí)鐘周期(振蕩周期)
在鴻蒙g_sysClock表示時(shí)鐘周期,是CPU的赫茲,也就是上面說的Cycle,這是固定不變的,由硬件晶振的頻率決定的. OsMain是內(nèi)核運(yùn)行的第一個(gè)C函數(shù),首個(gè)子函數(shù)就是 osRegister,完成對(duì)g_sysClock的賦值
CPU周期也叫(機(jī)器周期)
在鴻蒙宏OS_CYCLE_PER_TICK表示機(jī)器周期,Tick由用戶根據(jù)實(shí)際情況配置. 例如:主頻為1G的CPU,其振蕩周期為: 1吉赫 (GHz 109 Hz) = 1 000 000 000 Hz 當(dāng)Tick為100時(shí),則1 000 000 000/100 = 10000000 ,即一秒內(nèi)可產(chǎn)生1千萬個(gè)CPU周期.CPU就是用這1千萬個(gè)周期去執(zhí)行指令的.
指令周期
指令周期是執(zhí)行一條指令所需要的時(shí)間,一般由若干個(gè)機(jī)器周期組成。指令不同,所需的機(jī)器周期數(shù)也不同。 對(duì)于一些簡單的的單字節(jié)指令,在取指令周期中,指令取出到指令寄存器后,立即譯碼執(zhí)行,不再需要其它的機(jī)器周期。 對(duì)于一些比較復(fù)雜的指令,例如轉(zhuǎn)移指令、乘法指令,則需要兩個(gè)或者兩個(gè)以上的機(jī)器周期。 通常含一個(gè)機(jī)器周期的指令稱為單周期指令,包含兩個(gè)機(jī)器周期的指令稱為雙周期指令。
Tick硬中斷函數(shù)
- LITE_OS_SEC_BSS volatile UINT64 g_tickCount[LOSCFG_KERNEL_CORE_NUM] = {0};//tick計(jì)數(shù)器,系統(tǒng)一旦啟動(dòng),一直在++, 為防止溢出,這是一個(gè) UINT64 的變量
- LITE_OS_SEC_DATA_INIT UINT32 g_sysClock;//系統(tǒng)時(shí)鐘,是絕大部分部件工作的時(shí)鐘源,也是其他所有外設(shè)的時(shí)鐘的來源
- LITE_OS_SEC_DATA_INIT UINT32 g_tickPerSecond;//每秒Tick數(shù),鴻蒙默認(rèn)是每秒100次,即:10ms
- LITE_OS_SEC_BSS DOUBLE g_cycle2NsScale; //周期轉(zhuǎn)納秒級(jí)
- /* spinlock for task module */
- LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_tickSpin); //節(jié)拍器自旋鎖
- #define TICK_LOCK(state) LOS_SpinLockSave(&g_tickSpin, &(state))
- /*
- * Description : Tick interruption handler
- *///節(jié)拍中斷處理函數(shù) ,鴻蒙默認(rèn)10ms觸發(fā)一次
- LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
- {
- UINT32 intSave;
- TICK_LOCK(intSave);
- g_tickCount[ArchCurrCpuid()]++;//當(dāng)前CPU核計(jì)數(shù)器
- TICK_UNLOCK(intSave);
- #ifdef LOSCFG_KERNEL_VDSO
- OsUpdateVdsoTimeval();
- #endif
- #ifdef LOSCFG_KERNEL_TICKLESS
- OsTickIrqFlagSet(OsTicklessFlagGet());
- #endif
- #if (LOSCFG_BASE_CORE_TICK_HW_TIME == YES)
- HalClockIrqClear(); /* diff from every platform */
- #endif
- OsTimesliceCheck();//時(shí)間片檢查
- OsTaskScan(); /* task timeout scan *///任務(wù)掃描
- #if (LOSCFG_BASE_CORE_SWTMR == YES)
- OsSwtmrScan();//定時(shí)器掃描,看是否有超時(shí)的定時(shí)器
- #endif
- }
- #ifdef __cplusplus
- #if __cplusplus
- }
解讀
● g_tickCount記錄每個(gè)CPU核tick的數(shù)組,每次硬中斷都觸發(fā) OsTickHandler,每個(gè)CPU核單獨(dú)計(jì)數(shù).
● OsTickHandler是內(nèi)核調(diào)度的動(dòng)力,其中會(huì)檢查任務(wù)時(shí)間片是否用完,定時(shí)器是否超時(shí).主動(dòng)delay的任務(wù)是否需要被喚醒,其本質(zhì)是個(gè)硬中斷,在HalClockInit硬時(shí)鐘初始化時(shí)創(chuàng)建的,具體在硬中斷篇中會(huì)詳細(xì)講解.
● TICK_LOCK是tick操作的自旋鎖,宏原型LOS_SpinLockSave在自旋鎖篇中已詳細(xì)介紹.
功能函數(shù)
- #define OS_SYS_MS_PER_SECOND 1000 //一秒多少毫秒
- //獲取自系統(tǒng)啟動(dòng)以來的Tick數(shù)
- LITE_OS_SEC_TEXT_MINOR UINT64 LOS_TickCountGet(VOID)
- {
- UINT32 intSave;
- UINT64 tick;
- /*
- * use core0's tick as system's timeline,
- * the tick needs to be atomic.
- */
- TICK_LOCK(intSave);
- tick = g_tickCount[0];//使用CPU core0作為系統(tǒng)的 tick數(shù)
- TICK_UNLOCK(intSave);
- return tick;
- }
- //每個(gè)Tick多少Cycle數(shù)
- LITE_OS_SEC_TEXT_MINOR UINT32 LOS_CyclePerTickGet(VOID)
- {
- return g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND;
- }
- //毫秒轉(zhuǎn)換成Tick
- LITE_OS_SEC_TEXT_MINOR UINT32 LOS_MS2Tick(UINT32 millisec)
- {
- if (millisec == OS_MAX_VALUE) {
- return OS_MAX_VALUE;
- }
- return ((UINT64)millisec * LOSCFG_BASE_CORE_TICK_PER_SECOND) / OS_SYS_MS_PER_SECOND;
- }
- //Tick轉(zhuǎn)化為毫秒
- LITE_OS_SEC_TEXT_MINOR UINT32 LOS_Tick2MS(UINT32 tick)
- {
- return ((UINT64)tick * OS_SYS_MS_PER_SECOND) / LOSCFG_BASE_CORE_TICK_PER_SECOND;
- }
說明
● 在CPU篇中講過,0號(hào)CPU核默認(rèn)為主核,默認(rèn)獲取自系統(tǒng)啟動(dòng)以來的Tick數(shù)使用的是g_tickCount[0]
● 因每個(gè)CPU核的tick是獨(dú)立計(jì)數(shù)的,所以g_tickCount中各值是不一樣的.
● 系統(tǒng)的Tick數(shù)在關(guān)中斷的情況下不進(jìn)行計(jì)數(shù),因?yàn)镺sTickHandler本質(zhì)是由硬中斷觸發(fā)的,屏蔽硬中斷的情況下就不會(huì)觸發(fā)OsTickHandler,自然也就不會(huì)有g(shù)_tickCount[ArchCurrCpuid()]++的計(jì)數(shù),所以系統(tǒng)Tick數(shù)不能作為準(zhǔn)確時(shí)間使用.
● 追問下,什么情況下硬中斷會(huì)被屏蔽?
編程示例
前提條件:
● 使用每秒的Tick數(shù)LOSCFG_BASE_CORE_TICK_PER_SECOND的默認(rèn)值100。
● 配好OS_SYS_CLOCK系統(tǒng)主時(shí)鐘頻率。
時(shí)間轉(zhuǎn)換
- VOID Example_TransformTime(VOID)
- {
- UINT32 ms;
- UINT32 tick;
- tick = LOS_MS2Tick(10000); // 10000ms轉(zhuǎn)換為tick
- dprintf("tick = %d \n",tick);
- ms = LOS_Tick2MS(100); // 100tick轉(zhuǎn)換為ms
- dprintf("ms = %d \n",ms);
- }
時(shí)間轉(zhuǎn)換結(jié)果
- tick = 1000
- ms = 1000
時(shí)間統(tǒng)計(jì)和時(shí)間延遲
- LITE_OS_SEC_TEXT UINT32 LOS_TaskDelay(UINT32 tick);
- VOID Example_GetTime(VOID)
- {
- UINT32 cyclePerTick;
- UINT64 tickCount;
- cyclePerTick = LOS_CyclePerTickGet();
- if(0 != cyclePerTick) {
- dprintf("LOS_CyclePerTickGet = %d \n", cyclePerTick);
- }
- tickCount = LOS_TickCountGet();
- if(0 != tickCount) {
- dprintf("LOS_TickCountGet = %d \n", (UINT32)tickCount);
- }
- LOS_TaskDelay(200);//延遲200個(gè)tick
- tickCount = LOS_TickCountGet();
- if(0 != tickCount) {
- dprintf("LOS_TickCountGet after delay = %d \n", (UINT32)tickCount);
- }
- }
時(shí)間統(tǒng)計(jì)和時(shí)間延遲結(jié)果
- LOS_CyclePerTickGet = 495000 //取決于CPU的頻率
- LOS_TickCountGet = 1 //實(shí)際情況不一定是1的
- LOS_TickCountGet after delay = 201 //實(shí)際情況不一定是201,但二者的差距會(huì)是200
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)