鸿蒙内核源码分析(时间管理篇) | 谁是内核基本时间单位

embedded/2024/9/23 10:21:13/

时间概念太重要了,在鸿蒙内核又是如何管理和使用时间的呢?

时间管理以系统时钟 g_sysClock 为基础,给应用程序提供所有和时间有关的服务。

  • 用户以秒、毫秒为单位计时.
  • 操作系统以Tick为单位计时,这个认识很重要. 每秒的tick大小很大程度上决定了内核调度的次数多少.
  • 当用户需要对系统进行操作时,例如任务挂起、延时等,此时需要时间管理模块对Tick和秒/毫秒进行转换。

熟悉两个概念:

  • Cycle(周期):系统最小的计时单位。Cycle的时长由系统主时钟频率决定,系统主时钟频率就是每秒钟的Cycle数。
  • Tick(节拍):Tick是操作系统的基本时间单位,由用户配置的每秒Tick数决定,可大可小.

怎么去理解他们之间的关系呢?看几个宏定义就清楚了.

#ifndef OS_SYS_CLOCK	//HZ:是每秒中的周期性变动重复次数的计量
#define OS_SYS_CLOCK (get_bus_clk()) //系统主时钟频率 例如:50000000 即20纳秒震动一次
#endif
#ifndef LOSCFG_BASE_CORE_TICK_PER_SECOND
#define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 //每秒Tick数,意味着正常情况下每秒100次检查
#endif
#define OS_CYCLE_PER_TICK (g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND) //每个tick多少机器周期

时钟周期(振荡周期)

在鸿蒙g_sysClock表示时钟周期,是CPU的赫兹,也就是上面说的Cycle,这是固定不变的,由硬件晶振的频率决定的.
OsMain是内核运行的第一个C函数,首个子函数就是 osRegister,完成对g_sysClock的赋值

LITE_OS_SEC_TEXT_INIT VOID osRegister(VOID)
{g_sysClock = OS_SYS_CLOCK; //获取CPU HZ g_tickPerSecond =  LOSCFG_BASE_CORE_TICK_PER_SECOND;//每秒节拍数 默认100 即一个tick = 10msreturn;
}

CPU周期也叫(机器周期)

在鸿蒙宏OS_CYCLE_PER_TICK表示机器周期,Tick由用户根据实际情况配置.
例如:主频为1G的CPU,其振荡周期为: 1吉赫 (GHz 109 Hz) = 1 000 000 000 Hz
当Tick为100时,则1 000 000 000/100 = 10000000 ,即一个tick内可产生1千万个CPU周期.CPU就是用这1千万个周期去执行指令的.

指令周期

指令周期是执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期数也不同。

对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。

对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。

通常含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令。

Tick硬中断函数

LITE_OS_SEC_BSS volatile UINT64 g_tickCount[LOSCFG_KERNEL_CORE_NUM] = {0};//tick计数器,系统一旦启动,一直在++, 为防止溢出,这是一个 UINT64 的变量
LITE_OS_SEC_DATA_INIT UINT32 g_sysClock;//系统时钟,是绝大部分部件工作的时钟源,也是其他所有外设的时钟的来源 
LITE_OS_SEC_DATA_INIT UINT32 g_tickPerSecond;//每秒Tick数,鸿蒙默认是每秒100次,即:10ms
LITE_OS_SEC_BSS DOUBLE g_cycle2NsScale;	//周期转纳秒级/* spinlock for task module */
LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_tickSpin); //节拍器自旋锁
#define TICK_LOCK(state)                       LOS_SpinLockSave(&g_tickSpin, &(state))
/** Description : Tick interruption handler*///节拍中断处理函数 ,鸿蒙默认10ms触发一次
LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{UINT32 intSave;TICK_LOCK(intSave);g_tickCount[ArchCurrCpuid()]++;//当前CPU核计数器TICK_UNLOCK(intSave);#ifdef LOSCFG_KERNEL_VDSOOsUpdateVdsoTimeval();
#endif#ifdef LOSCFG_KERNEL_TICKLESSOsTickIrqFlagSet(OsTicklessFlagGet());
#endif#if (LOSCFG_BASE_CORE_TICK_HW_TIME == YES)HalClockIrqClear(); /* diff from every platform */
#endifOsTimesliceCheck();//时间片检查OsTaskScan(); /* task timeout scan *///任务扫描#if (LOSCFG_BASE_CORE_SWTMR == YES)OsSwtmrScan();//定时器扫描,看是否有超时的定时器
#endif
}#ifdef __cplusplus
#if __cplusplus
}

解读

  • g_tickCount记录每个CPU核tick的数组,每次硬中断都触发 OsTickHandler,每个CPU核单独计数.
  • OsTickHandler是内核调度的动力,其中会检查任务时间片是否用完,定时器是否超时.主动delay的任务是否需要被唤醒,其本质是个硬中断,在HalClockInit硬时钟初始化时创建的,具体在硬中断篇中会详细讲解.
  • TICK_LOCK是tick操作的自旋锁,宏原型LOS_SpinLockSave在自旋锁篇中已详细介绍.

功能函数

#define OS_SYS_MS_PER_SECOND   1000			//一秒多少毫秒
//获取自系统启动以来的Tick数
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作为系统的 tick数TICK_UNLOCK(intSave);return tick;
}
//每个Tick多少Cycle数
LITE_OS_SEC_TEXT_MINOR UINT32 LOS_CyclePerTickGet(VOID)
{return g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND;
}
//毫秒转换成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转化为毫秒
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号CPU核默认为主核,默认获取自系统启动以来的Tick数使用的是g_tickCount[0]
  • 因每个CPU核的tick是独立计数的,所以g_tickCount中各值是不一样的.
  • 系统的Tick数在关中断的情况下不进行计数,因为OsTickHandler本质是由硬中断触发的,屏蔽硬中断的情况下就不会触发OsTickHandler,自然也就不会有g_tickCount[ArchCurrCpuid()]++的计数,所以系统Tick数不能作为准确时间使用.
  • 追问下,什么情况下硬中断会被屏蔽?

编程示例

前提条件:

  • 使用每秒的Tick数LOSCFG_BASE_CORE_TICK_PER_SECOND的默认值100。
  • 配好OS_SYS_CLOCK系统主时钟频率。

时间转换

VOID Example_TransformTime(VOID)
{UINT32 ms;UINT32 tick;tick = LOS_MS2Tick(10000);    // 10000ms转换为tickdprintf("tick = %d \n",tick);ms = LOS_Tick2MS(100);        // 100tick转换为msdprintf("ms = %d \n",ms);
}

时间转换结果

tick = 1000
ms = 1000

时间统计和时间延迟

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个ticktickCount = LOS_TickCountGet();if(0 != tickCount) {dprintf("LOS_TickCountGet after delay = %d \n", (UINT32)tickCount);}
}

时间统计和时间延迟结果

LOS_CyclePerTickGet = 495000 //取决于CPU的频率
LOS_TickCountGet = 1 //实际情况不一定是1的
LOS_TickCountGet after delay = 201 //实际情况不一定是201,但二者的差距会是200

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH


http://www.ppmy.cn/embedded/39461.html

相关文章

[力扣题解]37. 解数独

题目&#xff1a;37. 解数独 思路 回溯法 代码 class Solution { public:bool function(vector<vector<char>>& board){int i, j;char k;for(i 0; i < 9; i){for(j 0; j < 9; j){// 为空if(board[i][j] .){for(k 1; k < 9; k){if(right(board…

C 语言中怎么产生真正的随机数?

在C语言中&#xff0c;要产生真正的随机数&#xff0c;我们通常使用标准库中的 <stdlib.h> 头文件中提供的随机数生成函数。 这些函数可以生成伪随机数&#xff0c;但它们在一定程度上是随机的&#xff0c;足以满足大多数应用程序的需求。 1. 伪随机数生成函数 C标准库…

【docker容器】 如何将docker 容器中的文件拷贝到本地

从 Docker 容器中拷贝文件到本地&#xff0c;步骤如下&#xff1a; 步骤 1: 确认容器正在运行 首先&#xff0c;使用以下命令检查容器是否正在运行&#xff1a; docker ps确保你找到了要拷贝文件的容器&#xff0c;并记下容器的 ID。 步骤 2: 使用 docker cp 命令拷贝文件 …

JVM进程缓存 Caffeine

JVM进程缓存 Caffeine 初识Caffeine Caffeine是一个基于Java8开发的&#xff0c;提供了近乎最佳命中率的高性能的本地缓存库。 ben-manes/caffeine: A high performance caching library for Java (github.com) 实例代码 Test void testBasicOps() {// 创建缓存对象Cache&…

PyCharm安装详细教程

PyCharm安装详细教程 PyCharm简介及其下载网站 PyCharm是由JetBrains打造的一款Python IDE(Integrated Development Environment&#xff0c;集成开发环境)&#xff0c;带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具。PyCharm提供了代码编辑、调试、语法高亮…

Vue 3 + Vite项目实战:常见问题与解决方案全解析

文章目录 一、项目使用本地图片打包后不显示1、在html中时候&#xff0c;本地运行和打包后线上运行都ok。2、用动态数据&#xff0c;本地运行ok&#xff0c;打包后线上运行不显示3、适用于处理单个链接的资源文件4、用动态数据且本地和线上访问都可显示 二、使用插件vite-plugi…

elementUI table表格相同元素合并行----支持多列

效果图如下: vue2代码如下&#xff1a; 只粘贴了js方法哦&#xff0c; methods: { // 设置合并行 setrowspans() {const columns [name, value]; // 需要合并的列名// 为每个需要合并的列设置默认 rowspan this.tableData.forEach(row > {columns.forEach(col > {row[…

unreal engine4 创建动画蒙太奇

UE4系列文章目录 文章目录 UE4系列文章目录前言一、创建动画蒙太奇 前言 动画蒙太奇的官方解释&#xff1a;Animation Montages are animation assets that enable you to combine animations in a single asset and control playback using Blueprints.You can use Animation…