本篇关键词:时钟周期、机器周期、指令周期、分频

下载 >> 离线文档.鸿蒙内核源码分析(百篇博客分析.挖透鸿蒙内核).pdf

基础知识相关篇为:

时间都去哪了 ?

  • 施教授说时间就是运动,地球围绕太阳公转一圈叫一年,地球自转一圈叫一天,地球自转了365天才围绕太阳转完了一圈,所以一年等于365天,这些都是运动。
  • 我们能感知到时间的流逝,大概也是因为我们身体在运动,细胞一次次的复制,衰老,死亡,直至最后一次再也无法复制代表着生命彻底终结。我们日常总是被时间催促着走,就这样一天又一天,一年再一年。疫情都第三年了,口罩也带习惯了,不敢回头看,常常感叹,时间都去哪了 ?还没好好感受年轻就老了,还没好好看看你眼睛就花了,转眼就只剩下满脸的皱纹了。哎呀 ! 老衲快不行了,要泪崩了 。。。

计算机怎么理解时间

  • 没有时间,我们的生活将是无序的,对计算机更是如此的,而且只会更敏感,否则如何能做到精确到纳秒级的运算,它是如何理解时间的呢?它的敏感源又是什么呢 ? 答案是:晶振 全称是石英晶体振荡器,是一种高精度和高稳定度的振荡器。位置一般在主板上,晶振为系统提供很稳定的脉冲信号,一秒钟内产生的脉冲次数也被称为系统时钟频率。一个标准的脉冲信号如 ( 图一 ) 所示。

  • 时钟周期 这里的时钟指的就是晶振,也叫晶振周期/振荡周期。它是计算机的最小时间单元,其他周期只能是它的倍数。

  • 机器周期 为什么要搞出来个机器周期,因为到了CPU操作层面时钟周期不好用,有点类似美元和人民币的关系,美元已经是世界货币了,但每个国家国情不同,操作起来还是得用本国货币方便。一个机器周期等于多个时钟周期。它是CPU完成一个基本操作的时间单元。比如: 取指、译码、存储器读/写、运算等等操作。

  • 指令周期 它是CPU完成一条指令的时间。又称提取-执行周期(fetch-and-execute cycle)是指CPU要执行一条机器指令经过的步骤,由若干机器周期组成。不同的机器分解指令周期的方式也不同,有的处理器对每条指令分解出相同数量的机器周期,另一些处理器根据指令的复杂程度分解出不同数量的机器周期,( 图二 )Cortex-A8处理器架构图,图中清晰的说明了指令从 取指 - > 译码 -> 执行 -> 储存四个阶段。

  • 总线周期 它是CPU操作总线设备的时间,由于存贮器和I/O端口是挂接在总线上的,对它们的访问,是通过总线实现的。通常将进行一次访问所需时间称为一个总线周期。这种访问速度较慢,因硬件发展的原因,各个设备的工作频率无法同步,甚至相差几个数量级,CPU很快,硬盘很慢,大家又需要协同工作,愉快的玩耍,就出现了分频概念,将高频信号变成低频信号,( 图三 ) 显示分频器分出来的四个频率代表二分频四分频八分频十六分频

  • 从时间长短角度总结下 时钟周期 < 机器周期 < 指令周期< 总线周期

周期 、Tick 、秒

  • 对于CPU来说,它的计时单位是Cycle(时钟周期),对于操作系统来说它的计时单位是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数,内核定时器触发单位
    #endif
    #define OS_CYCLE_PER_TICK (g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND) //每个tick时钟周期数
    
    1
    2
    3
    4
    5
    6
    7
    Tick由系统集成商根据实际情况来配置,Tick大系统更灵敏,对CPU的要求更高,也会带来更多的性能损耗。 从代码中可以计算出 OS_CYCLE_PER_TICK = 50万 个时钟周期,假设指令平均执行周期按10个时钟周期算(实际不能这么计算),可执行5万条机器指令。
  • CP15协处理器中有个专门的 CNTFRQ 寄存器用于存储时钟周期,详见代码:
    /* 
    * 见于 << arm 架构参考手册>> B4.1.21 处 CNTFRQ寄存器表示系统计数器的时钟频率。
    * 这个寄存器是一个通用的计时器寄存器。
    * MRC p15, 0, <Rt>, c14, c0, 0 ; Read CNTFRQ into Rt
    * MCR p15, 0, <Rt>, c14, c0, 0 ; Write Rt to CNTFRQ
    */
    UINT32 HalClockFreqRead(VOID)
    {
        return READ_TIMER_REG32(TIMER_REG_CNTFRQ);
    }
    
    VOID HalClockFreqWrite(UINT32 freq)
    {
        WRITE_TIMER_REG32(TIMER_REG_CNTFRQ, freq);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • 鸿蒙内核以全局变量g_sysClock表示系统时钟,由硬时钟模块完成对它的初始化,并创建硬定时器OsTickHandler
    //硬时钟初始化
    LITE_OS_SEC_TEXT_INIT VOID HalClockInit(VOID)
    {
        UINT32 ret;
    
        g_sysClock = HalClockFreqRead();//读取CPU的时钟频率
        ret = LOS_HwiCreate(OS_TICK_INT_NUM, MIN_INTERRUPT_PRIORITY, 0, OsTickHandler, 0);//创建硬中断定时器
        if (ret != LOS_OK) {
            PRINT_ERR("%s, %d create tick irq failed, ret:0x%x\n", __FUNCTION__, __LINE__, ret);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • 用户与操作系统之间的时间转换函数为
      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;
      }
      LITE_OS_SEC_TEXT_MINOR UINT32 LOS_Tick2MS(UINT32 tick)
      {
          return ((UINT64)tick * OS_SYS_MS_PER_SECOND) / LOSCFG_BASE_CORE_TICK_PER_SECOND;
      }
      LITE_OS_SEC_TEXT_MINOR UINT32 OsNS2Tick(UINT64 nanoseconds)
      {
          const UINT32 nsPerTick = OS_SYS_NS_PER_SECOND / LOSCFG_BASE_CORE_TICK_PER_SECOND;
          UINT64 ticks = (nanoseconds + nsPerTick - 1) / nsPerTick;
          if (ticks > OS_MAX_VALUE) {
              ticks = OS_MAX_VALUE;
          }
          return (UINT32)ticks;
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

Tick硬中断 | OsTickHandler


LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
#ifdef LOSCFG_SCHED_TICK_DEBUG 
    OsSchedDebugRecordData();
#endif
#ifdef LOSCFG_KERNEL_VDSO
    OsVdsoTimevalUpdate();//更新vdso数据页时间,vdso可以直接在用户进程空间绕过系统调用获取系统时间(例如:gettimeofday)
#endif
#ifdef LOSCFG_BASE_CORE_TICK_HW_TIME
    HalClockIrqClear(); /* diff from every platform */
#endif
    OsSchedTick();//由时钟发起的调度
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • OsTickHandler 是控制内核正常运行的发动机,本质是硬中断,在所有硬中断中优先级最低,注意没有硬中断内核将是一片死寂,毫无生气。硬件定时器是有时间规律的重复硬中断,中断处理函数反复的检查任务列表,是否当前任务继续执行,是否时间片到了该切换任务执行了,是否有更高优先级任务需先执行。同时检查软件定时器的任务列表的时间是否到了,可以说软件定时器的时间基础就是硬件定时器,其精确度是一个Tick
  • 涉及内容具体可翻看系列篇 中断管理篇调度机制篇软件定时器篇VDSO篇

百文说内核 | 抓住主脉络

  • 百文相当于摸出内核的肌肉和器官系统,让人开始丰满有立体感,因是直接从注释源码起步,在加注释过程中,每每有心得处就整理,慢慢形成了以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切。
  • 与代码需不断debug一样,文章内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。
  • 百文在 < 鸿蒙研究站 | 开源中国 | 博客园 | 51cto | csdn | 知乎 | 掘金 > 站点发布,百篇博客系列目录如下。

按功能模块:

基础知识 进程管理 任务管理 内存管理
双向链表
内核概念
源码结构
地址空间
计时单位
优雅的宏
钩子框架
位图管理
POSIX
main函数
调度故事
进程控制块
进程空间
线性区
红黑树
进程管理
Fork进程
进程回收
Shell编辑
Shell解析
任务控制块
并发并行
就绪队列
调度机制
任务管理
用栈方式
软件定时器
控制台
远程登录
协议栈
内存规则
物理内存
内存概念
虚实映射
页表管理
静态分配
TLFS算法
内存池管理
原子操作
圆整对齐
通讯机制 文件系统 硬件架构 内核汇编
通讯总览
自旋锁
互斥锁
快锁使用
快锁实现
读写锁
信号量
事件机制
信号生产
信号消费
消息队列
消息封装
消息映射
共享内存
文件概念
文件故事
索引节点
VFS
文件句柄
根文件系统
挂载机制
管道文件
文件映射
写时拷贝
芯片模式
ARM架构
指令集
协处理器
工作模式
寄存器
多核管理
中断概念
中断管理
编码方式
汇编基础
汇编传参
链接脚本
内核启动
进程切换
任务切换
中断切换
异常接管
缺页中断
编译运行 调测工具
编译过程
编译构建
GN语法
忍者无敌
ELF格式
ELF解析
静态链接
重定位
动态链接
进程映像
应用启动
系统调用
VDSO
模块监控
日志跟踪
系统安全
测试用例

百万注源码 | 处处扣细节

  • 百万汉字注解内核目的是要看清楚其毛细血管,细胞结构,等于在拿放大镜看内核。内核并不神秘,带着问题去源码中找答案是很容易上瘾的,你会发现很多文章对一些问题的解读是错误的,或者说不深刻难以自圆其说,你会慢慢形成自己新的解读,而新的解读又会碰到新的问题,如此层层递进,滚滚向前,拿着放大镜根本不愿意放手。

  • < gitee | github | coding | gitcode > 四大码仓推送 | 同步官方源码。

关注不迷路 | 代码即人生

期间不断得到小伙伴的支持,有学生,有职场新人,也有老江湖,在此一并感谢,大家的支持是前进的动力。尤其每次收到学生的赞助很感慨,后生可敬。 >> 查看捐助名单

据说喜欢 点赞 + 分享 的,后来都成了大神。😃