HarmonyOS内核源码分析(时间管理)|Tick是操作系统的基本时间单位在阅读本文之前,建议阅读鸿蒙内核源码分析(总目录)的其他文章。时间观念太重要了。鸿蒙内核是如何管理和使用时间的?时间管理基于系统时钟g_sysClock,为应用程序提供所有与时间相关的服务。●用户以秒和毫秒计数。●操作系统以滴答计数。这种理解非常重要。每秒的滴答大小在很大程度上决定了内核调度的次数。●当用户需要对系统进行操作时,如任务暂停、延迟等,此时时间管理模块需要对Tick和秒/毫秒进行转换。熟悉两个概念:●周期(period):系统的最小计时单位。一个周期的持续时间由主系统时钟的频率决定,即每秒的周期数。●Tick(节拍):Tick是操作系统的基本时间单位,由用户配置的每秒Ticks数决定,可大可小。如何理解它们之间的关系?看几个宏定义就清楚了。鸿蒙g_sysClock中的周期(振荡周期)代表时钟周期,也就是CPU的Hertz,也就是上面说的Cycle,由硬件晶振的频率固定决定。OsMain是内核运行的第一个C函数。第一个子函数是osRegister,完成对g_sysClock的赋值。CPU周期也称为(机器周期)。鸿蒙宏中OS_CYCLE_PER_TICK表示机器周期,Tick由用户根据实际情况配置。例如:主频为1G的CPU,其振荡周期为:1Gigahertz(GHz109Hz)=1000000000Hz当Tick为100时,则1000000000/100=10000000,即10一秒钟内可以生成百万个CPU周期。CPU用这1000万个Cycle来执行指令。指令周期指令周期是执行一条指令所需的时间,一般由若干个机器周期组成。不同的指令需要不同数量的机器周期。对于一些简单的单字节指令,在取指周期中,指令被取入指令寄存器后,会立即译码执行,不需要其他机器周期。对于一些更复杂的指令,如传送指令和乘法指令,需要两个或更多的机器周期。通常,包含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令。tickhardinterruptfunctionLITE_OS_SEC_BSSvolatileUINT64g_tickCount[LOSCFG_KERNEL_CORE_NUM]={0};//tick计数器,一旦系统启动,一直++,为了防止溢出,这是一个UINT64变量LITE_OS_SEC_DATA_INITUINT32g_sysClock;//系统时钟,是mostpartsworkLITE_OS_SEC_DATA_INITUINT32g_tickPerSecond;//每秒Ticks的次数,鸿蒙默认为每秒100次,即:10msLITE_OS_SEC_BSSDOUBLEG_cycle2NsScale;//循环到纳秒级别/*spinlockfortaskmodule*/LITE_OS_SEC_BSSSPIN_INCKtome();spinLOKMetron_INCKtome();(state)LOS_SpinLockSave(&g_tickSpin,&(state))/**说明:Tickinterruptionhandler*///Tick中断handler,鸿蒙默认10ms触发一次]++;//当CPU核处理器TICK_UNLOCK(intSave);#ifdefLOSCFG_KERNEL_VDSOOsUpdateVdsoTimeval();#endif#ifdefLOSCFG_KERNEL_TICKLESSOsTickIrqFlagSet(OsTicklessFlagGet());#endif#if(LOSCFG_BASE_CORE_TICK_HW_TIME==YES)HalClockIrqClear();/*difffromeveryplatform*/#endifOsTimesliceCheck();//时间片检查OsTaskScan();/*tasktimeoutscan*///任务扫描#if(LOSCFG_BASE_CORE_SWTMR==是的)OsSwtmrScan();//定时器扫描,看是否有超时定时器#endif}#ifdef__cplusplus#if__cplusplus}解释g_tickCount记录了每个CPU核心的ticks数组,每个硬中断触发OsTickHandler,每个CPU核心单独计数。●OsTickHandler是内核调度的驱动力,会检查任务时间片是否用完,定时器是否超时。激活的延时任务是否需要被唤醒本质上是一个硬中断,是在HalClockInit硬时钟初始化时创建的。会在硬中断章节详细讲解。TICK_LOCK是tick操作的自旋锁,LOS_SpinLockSave的宏原型在自旋锁章节已经详细介绍。Function#defineOS_SYS_MS_PER_SECOND1000//每秒多少毫秒//从系统启动后获取TicknumberLITE_OS_SEC_TEXT_MINORUINT64LOS_TickCountGet(VOID){UINT32intSave;UINT64tick;/**usecore0'stickassystem'stimeline,*thetickneedstobeatomic.*/TICK_LOCK(intSave);tick=g_tickCount[0];//UseCPUcore0asthesystemticknumber(TICKave_UNLO);returntick;}//每个Tick多少Cycle数LITE_OS_SEC_TEXT_MINORUINT32LOS_CyclePerTickGet(VOID){returng_sysClock/LOSCFG_BASE_CORE_TICK_PER_SECOND;}//毫秒转换成TickLITE_OS_SEC_TEXT_MINORUINT32LOS_MS2Tick(UINT32millisec){if(millisec==OS_MAX_VALUE){returnOS_MAX_VALUE;}return((UINT64)millisec*LOSCFG_BASE_CORE_TICK_PER_SECOND)/OS_SYS_MS_PER_SECOND;}//Tick转换为毫秒LITE_OS_SEC_TEXT_MINORUINT32LOS_Tick2MS(UINT32tick){return((UINT64)tick*OS_SYS_MS_PER_SECOND)/LOSCFG_BASE_CORE_TICK_PER_SECOND;}解释CPU章节中提到,CPU核心0默认为主核心,g_tickCount[0]●由于每个CPU核心的ticks是独立计数,g_tickCount中的每个值都是不同的。●关闭中断时不统计系统的Tick数,因为OsTickHandler本质上是由硬件中断触发的。硬中断被屏蔽后,OsTickHandler不会被触发,自然也就没有g_tickCount[ArchCurrCpuid()]++计数,所以系统Tick数不能作为准确时间。盾?编程示例先决条件:●使用默认值TickspersecondLOSCFG_BASE_CORE_TICK_PER_SECOND100●配置OS_SYS_CLOCK系统主时钟频率。时间转换VOIDEExample_TransformTime(VOID){UINT32ms;UINT32tick;tick=LOS_MS2Tick(10000);//10000ms转换为tickdprintf("tick=%d\n",tick);ms=LOS_Tick2MS(100);//100tick转换tomsdprintf("ms=%d\n",ms);}时间转换结果tick=1000ms=1000时间统计和时间延迟(0!=cyclePerTick){dprintf("LOS_CyclePerTickGet=%d\n",cyclePerTick);}tickCount=LOS_TickCountGet();if(0!=tickCount){dprintf("LOS_TickCountGet=%d\n",(UINT32)tickCount);}LOS_TaskDelay(200);//延迟200ticktickCount=LOS_TickCountGet();if(0!=tickCount){dprintf("LOS_TickCountGetafterdelay=%d\n",(UINT32)tickCount);}}时间统计和timedelay结果LOS_CyclePerTickGet=495000//取决于CPU的频率LOS_TickCountGet=1//实际情况不一定是1LOS_TickCountGetafterdelay=201//实际情况不一定是201,但是两者之间的差距会是200。更多信息请访问:https://harmonyos.51cto.com,与华为共同打造的鸿蒙技术社区
