28、【OS】【Nuttx】最小系统初始化分析(4):定时器(二)
背景
接上篇wiki
27、【OS】【Nuttx】最小系统初始化分析(4):定时器(一)
分析了定时器初始化过程,以及初始化生成的定时器实例,并着重分析了实例对象里的 sim_current 方法,接下来对最小系统中,定时器的启动,以及执行的任务进行分析
定时器启动
来看定时器启动函数 sim_start,这里有两个关键点:
- 该接口是一次性的(oneshot),定时结束后,如果想要继续定时,需再次调用该函数,重新启动一个新的定时器(其实是一次性接口还是永久接口由写代码的人决定的,只要不重置定时器的超时时间,就是永久定时器)
- 该接口所启动的定时器为软件定时器,非硬件定时器。这个也好理解,软件定时器通过调度进行定时,硬件定时器依赖芯片硬件实现,一般超时通过中断通知用户。通常情况下,调度任务一般由软件定时器负责
入参有四个参数:
- 定时器实例(没啥好讲的,上篇wiki有分析)
- handler:处理函数,回调函数(有英文翻译成句柄?一直以来觉得句柄这个翻译很抽象)
- arg:回调函数入参
- ts:定时时间
启动函数分析:主要就是获取下当前时间,然后设置一个超时时间(priv->alarm),并填好回调函数以及回调函数入参,比较简单。
这里注意下上面划红线的两个地方:进入临界区和退出临界区,操作定时器对象应该是个原子操作,因为涉及到三个成员的改动,可能会出现如下情况:用户A正在使用 sim_start 操作定时器,此时用户B通过中断进来,也想操作定时器,结果用户A操作一半的时候,被用户B抢走了使用权,并把成员都修改成了用户B的期望值,结果就是最终得到的结果既不是用户A的期望值,也不是用户B的期望值,而是用户A和用户B混合的一个非预期值。
加入临界区保护后,效果就是用户进入临界区后,才对定时器进行操作,此时用户B的中断无法抢占用户A,只有当用户A退出临界区后,用户B的中断才可操作定时器,此时最终结果可以预料到是用户B的期望值(这里只简单举个例子,实际软件设计时,应当要考虑用户A被抢占的可能性,并应该有相应的失效机制)
定时器任务
可以看到,定时器初始化时,分配的回调函数为 oneshot_callback,这里可以看到,启动定时器时,使用的启动方式为 ONESHOT_TICK_START,间隔时间为1个tick,之前分析过,每个tick间隔为10ms,故该定时器的回调函数执行时间为10ms之后
由于定时器为软件定时器,需要有任务不断查询定时器状态,以判断定时器是否超时,当然,这个任务落在 idle_task 身上
所以软件定时器的执行过程如下所示
上篇wiki分析过,软件定时器里面执行的任务,有个看门狗
其实该看门狗定时器,也是一个软件定时器,可以用来处理一些与信号相关的操作,比如在休眠函数 usleep 中,会根据 usleep 所休眠的时间,用看门狗启动一个看门狗定时器,上面的定时器任务会判断该看门狗定时器是否超时,若超时,则跳转至看门狗的超时函数 nxsig_timeout 中(很奇怪,看门狗定时器的超时判断依赖别的定时器任务?直接放到 idle_task 任务里面判断就好了,或许是为了防止用户忘记调用看门狗的处理函数?所以直接放到 oneshot_callback 里面 )
软件看门狗的超时判断关系如下