整体思想
- 前几章的线程延时是通过线程的
remaining_tick
属性来记录延时时间,在 SysTick 中断中扫描并更新每个线程的remaining_tick
,如果remaining_tick
等于0,将该线程就绪,时间复杂度O(n);而定时器的策略是,每个线程都有自己的定时器,当线程需要延时时,先将线程挂起,然后启动内置的定时器,并将定时器挂到一个系统维护的全局的定时器列表rt_timer_list
,每个节点代表了正在延时的线程的定时器,节点按照延时时间大小做升序排列,所以时基更新时只用扫描系统定时器列表的第一个节点的timeout_tick
,如果第一个定时器未到期,则后面的也不用判断,时间复杂度O(1)。定时器结构体如下,struct rt_timer { struct rt_object parent; /* 从 rt_object 继承 */ rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL]; /* 节点(跳跃表) */ void (*timeout_func)(void *parameter); /* 超时函数 */ void *parameter; /* 超时函数形参 */ rt_tick_t init_tick; /* 定时器实际需要延时的时间 */ rt_tick_t timeout_tick; /* 定时器实际超时时的系统节拍数 = rt_tick + init_tick */ }; typedef struct rt_timer *rt_timer_t;
- 结构体成员
rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL];
是数据结构跳跃表(skip list)的应用,一种以空间换时间的查找策略,后面有详细点的说明。 - 结构体成员
void (*timeout_func)(void *parameter);
定时器超时后调用该函数; - 结构体成员
rt_tick_t timeout_tick;
其值timeout_tick = rt_tick + init_tick,rt_tick
是该定时器启动时系统的tick数值,加上init_tick
,表示定时器超时时,系统tick的数值。 - 使用定时器要先初始化系统定时器列表。
rt_system_timer_init();
- 定时器初始化函数
rt_timer_init()
,在线程初始化时调用。
/**
* 该函数用于初始化一个定时器,通常该函数用于初始化一个静态的定时器
*
* @param timer 静态定时器对象
* @param name 定时器的名字
* @param timeout 超时函数
* @param parameter 超时函数形参
* @param time 定时器的超时时间
* @param flag 定时器的标志
*/
void rt_timer_init(rt_timer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
/* 定时器对象初始化 */
rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);
/* 定时器初始化 */
_rt_timer_init(timer, timeout, parameter, time, flag);
}
启动定时器
- rt_err_t rt_timer_start(rt_timer_t timer); 这里有个宏
RT_TIMER_SKIP_LIST_LEVEL
,表示定时器跳跃表等级,默认是1。这个函数会更新该定时器标志parent.flag
和超时时间timeout_tick
,然后在系统定时器列表中找到合适的位置插入该定时器节点。
/**
* 启动定时器,并将定时器挂到定时器列表
*
* @param timer 将要启动的定时器
*
* @return 操作状态, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_timer_start(rt_timer_t timer)
{
unsigned int row_lvl = 0;
rt_list_t *timer_list;
register rt_base_t level;
rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL]