v31.02 鸿蒙内核源码分析(定时器) | 哪个任务的优先级最高 原创 精华
子畏于匡。曰:“文王既没,文不在兹乎?天之将丧斯文也,后死者不得与于斯文也;天之未丧斯文也,匡人其如予何?” 《论语》:子罕篇
百篇博客系列篇.本篇为:
v31.xx 鸿蒙内核源码分析(定时器篇) | 哪个任务的优先级最高
基础工具相关篇为:
- v01.12 鸿蒙内核源码分析(双向链表) | 谁是内核最重要结构体
- v19.04 鸿蒙内核源码分析(位图管理) | 谁能一分钱分两半花
- v20.03 鸿蒙内核源码分析(用栈方式) | 程序运行场地由谁提供
- v31.02 鸿蒙内核源码分析(定时器) | 哪个任务的优先级最高
- v34.04 鸿蒙内核源码分析(原子操作) | 谁在为原子操作保驾护航
- v35.03 鸿蒙内核源码分析(时间管理) | 谁是内核基本时间单位
本篇说清楚定时器的实现
读本篇之前建议先读鸿蒙内核源码分析(总目录)其余篇.
运作机制
- 软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器。当经过设定的Tick数后,会触发用户自定义的回调函数。
- 软件定时器是系统资源,在模块初始化的时候已经分配了一块连续内存。
- 软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先触发的准则。
- 软件定时器以Tick为基本计时单位,当创建并启动一个软件定时器时,鸿蒙会根据当前系统Tick时间及设置的定时时长确定该定时器的到期Tick时间,并将该定时器控制结构挂入计时全局链表。
- 当Tick中断到来时,在Tick中断处理函数中扫描软件定时器的计时全局链表,检查是否有定时器超时,
- 若有则将超时的定时器记录下来。Tick中断处理函数结束后,软件定时器任务(优先级为最高)被唤醒,在该任务中调用已经记录下来的定时器的回调函数。
定时器长什么样?
解读
-
在多CPU核情况下,定时器是跟着CPU走的,每个CPU核都维护着独立的定时任务链表,上面挂的都是CPU核要处理的定时器.
-
stSortList
的背后是双向链表,这对钩子在定时器创建的那一刻会钩到CPU的swtmrSortLink
上去. -
pfnHandler
定时器时间到了的执行函数,由外界指定.uwArg
为回调函数的参数 -
ucMode
为定时器模式,软件定时器提供了三类模式单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动删除。
周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动停止定时器,否则将永远持续执行下去。
单次触发定时器,但这类定时器超时触发后不会自动删除,需要调用定时器删除接口删除定时器。 -
ucState
定时器状态.OS_SWTMR_STATUS_UNUSED(定时器未使用)
系统在定时器模块初始化时,会将系统中所有定时器资源初始化成该状态。
OS_SWTMR_STATUS_TICKING(定时器处于计数状态)
在定时器创建后调用LOS_SwtmrStart接口启动,定时器将变成该状态,是定时器运行时的状态。
OS_SWTMR_STATUS_CREATED(定时器创建后未启动,或已停止)
定时器创建后,不处于计数状态时,定时器将变成该状态。
定时器分类
定时器是指从指定的时刻开始,经过一定的指定时间后触发一个事件,例如定个时间提醒晚上9点准时秒杀。定时器有硬件定时器和软件定时器之分:
-
硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
-
软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。
鸿蒙内核提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是 OS Tick 的整数倍,例如鸿蒙内核默认是10ms触发一次,那么上层软件定时器只能是 10ms,20ms,100ms 等,而不能定时为 15ms。
定时器怎么管理?
解读
三个全局变量可知,定时器是通过池来管理,在初始化阶段赋值.
g_swtmrCBArray
定时器池,初始化中一次性创建1024个定时器控制块供使用g_swtmrHandlerPool
回调函数池,回调函数也是统一管理的,申请了静态内存保存. 池中放的是SwtmrHandlerItem
回调函数描述符.g_swtmrFreeList
空闲可供分配的定时器链表,鸿蒙的进程池,任务池,事件池都是这么处理的,没有印象的自行去翻看.g_swtmrFreeList
上挂的是一个个的SWTMR_CTRL_S
- 要搞明白
SWTMR_CTRL_S
和SwtmrHandlerItem
的关系,前者是一个定时器,后者是定时器时间到了去哪里干活.
初始化 -> OsSwtmrInit
代码解读:
- 每个CPU核都是独立处理定时器任务的,所以需要独自管理.
OsSwtmrInit
是负责初始化各CPU核定时模块功能的,注意在多CPU核时,OsSwtmrInit
会被多次调用. cpuid == 0
代表主CPU核, 它最早执行这个函数,所以g_swtmrCBArray
和g_swtmrHandlerPool
是共用的,系统默认最多支持 1024 个定时器和回调函数.- 每个CPU核都创建了自己独立的
LOS_QueueCreate
队列和任务OsSwtmrTaskCreate
,并初始化了swtmrSortLink
链表,关于链表排序可前往系列篇总目录 排序链表篇查看.
定时任务 -> 最高优先级
代码解读:
- 内核为每个CPU处理单独创建任务来处理定时器, 任务即线程, 外界可理解为内核开设了一个线程跑定时器.
- 注意看任务的优先级
swtmrTask.usTaskPrio = 0;
0是最高优先级! 这并不多见! 内核会在第一时间响应软时钟任务. - 系列篇CPU篇中讲过每个CPU都有自己的任务链表和定时器任务,
g_percpu[cpuid].swtmrTaskID = swtmrTaskID;
表示创建的任务和CPU具体核进行了捆绑.从此swtmrTaskID负责这个CPU的定时器处理. - 定时任务是一个系统任务,除此之外还有哪些是系统任务?
- 任务入口函数
OsSwtmrTask
,是任务的执行体,类似于[Java 线程中的run()函数] usCpuAffiMask
代表这个任务只能由这个CPU核来跑
队列消费者 -> OsSwtmrTask
代码解读:
- OsSwtmrTask是任务的执行体,只做一件事,消费定时器回调函数队列.
- 任务在跑一个死循环,不断在读队列.关于队列的具体操作不在此处细说,系列篇中已有专门的文章讲解,可前往查看.
- 每个CPU核都有属于自己的定时器回调函数队列,里面存放的是时间到了回调函数.
- 但队列的数据怎么来呢?
OsSwtmrTask
只是在不断的消费队列,那生产者在哪里呢? 就是OsSwtmrScan
队列生产者 -> OsSwtmrScan
代码解读:
- OsSwtmrScan 函数是在系统时钟处理函数
OsTickHandler
中调用的,它就干一件事,不停的比较定时器是否超时 - 一旦超时就把定时器的回调函数扔到队列中,让
OsSwtmrTask
去消费.
总结
-
定时器池
g_swtmrCBArray
存储内核所有的定时器,默认1024个,各CPU共享这个池 -
定时器响应函数池
g_swtmrHandlerPool
存储内核所有的定时器响应函数,默认1024个,各CPU也共享这个池 -
每个CPU核都有独立的任务(线程)来处理定时器, 这个任务叫定时任务
-
每个CPU核都有独立的响应函数队列
swtmrHandlerQueue
,队列中存放该核时间到了的响应函数SwtmrHandlerItem
-
定时任务的优先级最高,循环读取队列
swtmrHandlerQueue
,swtmrHandlerQueue
中存放是定时器时间到了的响应函数.并一一回调这些响应函数. -
OsSwtmrScan负责扫描定时器的时间是否到了,到了就往队列
swtmrHandlerQueue
中扔. -
定时器有多种模式,包括单次,循环.所以循环类定时器的响应函数会多次出现在
swtmrHandlerQueue
中.
百万汉字注解.精读内核源码
百篇博客分析.深挖内核地基
给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,.xx
代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。
基础工具>> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
加载运行>> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
进程管理>> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
编译构建>> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
进程通讯>> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
内存管理>> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
前因后果>> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
任务管理>> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
文件系统>> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
硬件架构>> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。
楼主形容的很贴切
老百姓都喜欢看宫廷剧,都羡慕着咱皇上呢. : )
👍👍👍
感谢! 张老师五一没出去玩啊