摘要

软件定时器在协议栈等很多场景都有广泛的应用,有时候会有大量的定时器同时处于工作状态,它们的超时时间各异,要高效的保证每个定时器都能够较为准确的超时并执行到其回调函数并不是一件易事。本文分析嵌入式实时操作系统Nucleus的定时器方案,它巧妙的管理了一条按照相对时间来排序的双向链表,避免每次tick中断都要遍历链表检查超时和更新剩余时间,实现了一种相当高效的软件定时器。

数据结构分析

结构体TM_TCB来表示动态创建的定时器,其定义如下

typedef struct TM_TCB_STRUCT

{

/*Nucleus的定时器分为应用定时器和task内置两种type  */

INT                 tm_timer_type;

/* 剩余时间,可动态改变  */

UNSIGNED            tm_remaining_time;

/*tm_information指向TM_APP_TCB,其保存了超时函数等定时器参数  */

VOID               *tm_information;

struct TM_TCB_STRUCT

*tm_next_timer,           /* Next timer in list    */

*tm_previous_timer;     /* Previous timer in list*/

} TM_TCB;

每个字段都有注释,和本文相关的字段已经标红。

定时器结构体TM_TCB之间使用双向循环链表的方式组织在一起,即tm_next_timer和tm_previous_timer指针。Nucleus里有个TM_TCB    *TMD_Active_Timers_List全局指针,其指向该链表的头部,管理着所有处于激活状态的定时器。

假设当前系统里没有定时器,我们的应用在初始化时要连续创建和激活5个周期性的定时器,其周期超时时间为:5,8,8,12,20(单位都是tick).

假设是按照顺序创建,那么链表的形成过程如下所示(方框里的数字就是TM_TCB的tm_remaining_time):

 //第1个定时器会在5个tick之后超时

 // 第2个定时器会在8个tick之后超时,相对于前一个,它的超时时间多3

 // 第3个定时器会在8个tick之后超时,相对于前一个,它的超时时间多0

 // 第4个定时器会在12个tick之后超时,相对于前一个,它的超时时间多4

 // 第5个定时器会在20个tick之后超时,相对于前一个,它的超时时间多8

看起来很容易。

现在打乱其创建的顺序,例如12,8,20,5,8,那么链表的形成过程如下所示(蓝色表示新插入的节点,棕色表示受影响的节点):

        //第1个定时器会在12个tick之后超时

      // 第1个定时器会在8个tick之后超时,第2个相对于前一个,它的超时时间多4

    // 第3个定时器会在20个tick之后超时,相对于前一个,它的超时时间多8

    //链表重新排序,并更新当前插入的定时器的下一个元素的相对时间

     //链表重新排序,并更新当前插入的定时器的下一个元素的相对时间

总之,无论创建的顺序如何,结果链表组织方式和顺序都是一样的

时间的递减怎么记录?

需要引入一个重要的全局变量

UNSIGNED TMD_Timer

它记录了当前距离超时最近的定时器的剩余时间。例如在上面的例子里,当前TMD_Timer值为5。Nucleus的底层实现了一个中断处理函数INT_Timer_Interrupt,它是由硬件定时器触发的,当有处于激活状态的timer时,每个tick触发一次,在这个函数里,会对TMD_Timer减1并判断是否为0,如果为0,说明有定时器超时,开始激活TMD_HISR,其回调函数就是用于处理所有定时器超时的入口。

有定时器超时了以后会发生什么?

假设现在已经过了5个tick(这个5个tick里,不需要对定时器链表有任何的操作),那么首节点的超时函数会被执行,首节点被从链表头部摘除,TMD_Timer会被改写为下一个首节点的剩余时间重新计数,即3.且链表会更新成(蓝色表示新插入的节点,棕色表示受影响的节点):

原首节点定时器是周期性的,所以需要重新寻找合适的位置插入链表,由于它的下次超时时间是5个tick后.所以应该放在3之后,且相对超时时间为2。同时受到影响的还有它的下一个节点,原来的相对剩余时间是4,由于加入了一个新的节点,相对的剩余时间变成了2。

假设又继续过了3个tick,现在的第一、二个节点会超时(在一次激活HISR的函数里执行两个定时器的超时函数),TMD_Timer会被改写为下一个首节点的剩余时间重新计数,即2,链表会被更新为

再过2个tick,下个节点超时,以此类推。

动态加入定时器

前面创建几个定时器的时候都是同时创建的,如果不是同时创建会怎么样?

如上面的分析,回到整个用例开始的时候,在5个定时器组成的链表建立好之后,在接下来的5个tick内,没有定时器会超时,同时链表里的数据也不会有任何更新。

假设在3个tick之后,需要加入一个新的定时器,超时时间为10,应该怎么办?

如果继续按照上面组织链表的办法,10 = 5 + 3 + 2,应该放在第4位,且相对前一个定时器的超时时间加2,如下所示:

但是请注意,当前已经距离开始时间已经过去了3个tick,第一个定时器会在2个tick后超时,第二和第三个会在5秒后超时,所以如果按照这种方式,新加入的这个定时器在7个tick后就超时了,这显然是不正确的。

这个地方还是需要TMD_Timer,它记录了当前距离超时最近的定时器的剩余时间,也就是等于当前正确的链表首节点的剩余时间(开始是5,过了3个tick之后,变成了2)

正确的做法是先更新头节点的剩余时间之后,再插入新的节点,更新完之后的链表如下所示

这样,链表里的每个定时器节点都会有正确的超时时间了

停止定时器

有了前面的分析,停止定时器时的链表操作就很简单了,紧接着上面的例子,假设1个tick后,应用程序停止了第5个timer(图中的剩余时间为1),链表更新如下:

停止定时器最重要的工作就是将其从TMD_Active_Timers_List链表上摘下,摘节点时不需要更新头节点的剩余时间,但需要更新要摘除的节点的下一个节点的剩余时间8 = 7 + 1

最后还有两个问题:

1 这个定时器的设计把耗时的操作都放到了start 和stop timer的时刻。当timer个数很多时,这个新timer节点的插入以及整个链表的会不会比较费时?

答:1 前面的这些讨论都是关于在激活状态的定时器,实际使用中,在同一时间里,会有相当部分的定时器是处于非激活状态,它们不需要用链表组织起来(应用程序拥有其指针可对其进行操作)。所以激活定时器组成的链表长度不会太长。

2将start timer的操作分解开来,就可以看出它只是干了几件事(stop也是类似的):

a)更新头节点的剩余超时时间为TMD_Time。固定时间,几条指令即可完成

b)遍历链表,找到合适的插入位置。这是一个相对耗时的操作,时间复杂度是O(n)

c)更新这个插入位置的下一个节点的剩余时间,也就是图里面的棕色节点, 固定时间,几条指令即可完成

2 这个定时器的设计还能再优化吗?

答:软件层面上,这是楼主见过的最优秀的定时器设计方案,硬件上还有一点优化空间。前面对于硬件定时计数的描述是这样的

每个tick触发一次,在这个函数里,会对TMD_Timer减1并判断是否为0,如果为0,说明有定时器超时,开始激活TMD_HISR

这是一个反复比较的过程,每个tick的中断里,会做不多的数字累加和比较操作,这个定时器中断的操作可以省下来。每次链表头节点更新的时候,可以启动一个硬件定时器,将其超时时间设置为TMD_Timer,时间一到,就代表着头节点已超时,而不用反复的在每个tick都做着重复的工作

高效软件定时器的设计相关推荐

  1. STM32软件定时器的设计

    定时器分为硬件定时器和软件定时器,几乎所有的微控制器上都配备了数量有限的硬件定时器,即控制器本身有专门实现定时的模块.几乎所有的硬件定时器的工作原理都是一样的:定时器在外部时钟提供的周期脉冲下进行计数 ...

  2. c语言定时器原理,软件定时器实现原理、测试及应用设计

    分析μC/OSII操作系统软件定时器的算法,以及影响定时器精度的抖动问题产生的原因:提出定时器管理任务处理器占用率的计算公式,并进行了实验测试.实验结果表明,μC/OSII软件定时器具有ms级的最小分 ...

  3. μC/OS-II软件定时器的分析与测试

     引 言 μC/OS-II操作系统是建立在微内核基础上的实时操作系统,抢占式多任务.微内核.移植性好等特点,使其在诸多领域都有较好的应用. 在μC/OS-II 2.83及其以后的版本中,一个较大的 ...

  4. 时间就是金钱!Windows 上必装的 10 款高效软件

    点击上方"视学算法",选择"星标"公众号 重磅干货,第一时间送达 来源 | 资源搜罗精选 不管你是上班族还是学生党,是电脑迷还是技术派,都会常跟电脑打交道.今天 ...

  5. 高质高效软件开发组织能力模型

    背景 至今,我在Motorola网络部工作超过了5年,所在的产品线也是采用统一软件开发过程和敏捷思想(但不是SCRUM)来组织软件开发活动的,但这5年多的工作经历从未引起我象微博上对于SCRUM话题的 ...

  6. android定时器课程设计,定时器课程设计.doc

    定时器课程设计定时器课程设计 摘要: 本设计通过使用89C52RC芯片核心,通过P3.4-P3.7口控制按键录入时间,P0.0-P0.7控制LED数码显示器时间,P2.3控制蜂鸣器定时器到达指定时间报 ...

  7. 如何用计算机环境设计,计算机绘图软件在环境艺术设计中运用.doc

    计算机绘图软件在环境艺术设计中运用 计算机绘图软件在环境艺术设计中运用 随着计算机软.硬件技术的快速发展,计算机辅助设计在环境艺术设计(以下简称"环艺设计")领域受到了广泛的重视和 ...

  8. ESP32-C3入门教程 基础篇(六、TIMG 硬件定时器 与 软件定时器)

    到了测试第6课,还没有玩过ESP32-C3的基本定时器,虽然FreeRTOS,可以使用软件定时器 但是软件定时器毕竟也有不适用的时候,这个在我FreeRTOS博文中有单独说明. 所以硬件定时器也得熟悉 ...

  9. 关于软件定时器的一些讨论

    1.简介 这里先介绍下软件定时器和硬件定时器的区别 硬件定时器: CPU内部自带的定时器模块,通过初始化.配置可以实现定时,定时时间到以后就会执行相应的定时器中断处理函数.硬件定时器一般都带有其它功能 ...

最新文章

  1. GitHub上YOLOv5开源代码的训练数据定义
  2. 小白程序员仅用5分钟入职BAT,他只做了这件事!
  3. IDEA中添加tomcat服务器和创建一个新的web项目
  4. mysql 7下载安装及问题解决
  5. Java高并发编程详解系列-线程上下文设计模式及ThreadLocal详解
  6. 康乐主机系统怎么安装php,Kangle EP面板详细安装说明
  7. 怎样对付win7黑屏
  8. Java IO实战操作(一)
  9. Android Activity绑定到Service
  10. linux 虚拟机设置静态ip
  11. 【问题记录】python 函数 传入一个对象返回一个对象值得注意
  12. GCC 原子操作中 Acquire/Release/Consume/Relaxed 内存模型
  13. 恶魔和梦魇的私语------- 关于软件开发的务虚主义对话(3)
  14. xsmax无法进入dfu模式_iPhoneXS/XR:强制重启,进入DFU、恢复模式
  15. 调用微信公众号现金红包接口给用户发送红包
  16. matplotlib 笔记: contourf contour
  17. 看网易云公开课代码性能开发有感,怎样提高代码的性能
  18. h5页面调用百度地图获取当前位置并在地图上标注出来
  19. 雇佣兵战斗力c语言原理,暗黑2单机弓箭亚马逊应该怎么选技能和雇佣兵
  20. PL/SQL用户指南与参考(第一章)

热门文章

  1. Qt几个月的学习心得及展望
  2. 2023 华为 Datacom-HCIE 真题题库 12/12(完结)--含解析
  3. 有赞大裁员,竟把人员优化写进 OKR
  4. python 按键精灵脚本_[620]使用Python实现一个按键精灵
  5. 2023年网络安全比赛--Web综合渗透测试中职组(超详细)
  6. 基于Predictive Parsing的ABNF语法分析器(十二)——alternation、concatenation、group和option
  7. pandas读写表格
  8. linux下deb包如何安装
  9. [Error] expected primary-expression before ‘%’
  10. cv2 python 获取斑马线_opencv 斑马线,条纹检测