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_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];
unsigned int tst_nr;
static unsigned int random_nr;
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 将定时器从系统定时器列表移除 */
_rt_timer_remove(timer);
/* 改变定时器的状态为非active */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
/* 开中断 */
rt_hw_interrupt_enable(level);
/* 获取 timeout tick,
最大的timeout tick 不能大于 RT_TICK_MAX/2 */
timer->timeout_tick = rt_tick_get() + timer->init_tick;
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 将定时器插入到定时器列表 */
/* 获取系统定时器列表根节点地址,rt_timer_list是一个全局变量 */
timer_list = rt_timer_list;
/* 获取系统定时器列表第一条链表根节点地址 */
row_head[0] = &timer_list[0];
/* 因为RT_TIMER_SKIP_LIST_LEVEL等于1,这个循环只会执行一次 */
for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
/* 列表不为空,当没有定时器被插入到系统定时器列表时,该循环不执行 */
/* RT_TIMER_SKIP_LIST_LEVEL = 1, 则row_lvl始终等于0
* 当 row_head[0]->timeout_tick < timer->timeout_tick 时更新row_head[0]的值,即向后排
*/
for (; row_head[row_lvl] != timer_list[row_lvl].prev; row_head[row_lvl] = row_head[row_lvl]->next)
{
struct rt_timer *t;
/* 获取定时器列表节点地址 */
rt_list_t *p = row_head[row_lvl]->next;
/* 根据节点地址获取父结构的指针 */
t = rt_list_entry(p, /* 节点地址 */
struct rt_timer, /* 节点所在父结构的数据类型 */
row[row_lvl]); /* 节点在父结构中叫什么,即名字 */
/* 两个定时器的超时时间相同,则继续在定时器列表中寻找下一个节点 */
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
/* uint 相减 */
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
{
break;
}
}
/* RT_TIMER_SKIP_LIST_LEVEL等于1时,条件不会成真,不会被执行 */
/* 如果RT_TIMER_SKIP_LIST_LEVEL不等于1,这里更新到下一层节点,继续向后找 */
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
{
row_head[row_lvl + 1] = row_head[row_lvl] + 1; // ------------------------------ ⑴
}
}
/* random_nr是一个静态变量,用于记录启动了多少个定时器 */
/* Interestingly, this super simple timer insert counter works very very
* well on distributing the list height uniformly. By means of "very very
* well", I mean it beats the randomness of timer->timeout_tick very easily
* (actually, the timeout_tick is not random and easy to be attacked). */
random_nr++;
tst_nr = random_nr;
/* 将定时器插入到系统定时器列表 */
/* 假如只有一个定时器列表,row_head[0]表示上面找到的合适的插入位置 */
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1], /* 双向列表根节点地址 */
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); /* 要被插入的节点的地址 */ // ---------- ⑵
/* RT_TIMER_SKIP_LIST_LEVEL 等于1,该for循环永远不会执行 */
/* 这里是为了跳跃表的随机插入 */
for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl])); // ----------------------------- ⑶
else
break;
tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
}
/* 设置定时器标志位为激活态 */
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
/* 开中断 */
rt_hw_interrupt_enable(level);
return -RT_EOK;
}
RT_TIMER_SKIP_LIST_LEVEL
为1,timer_list
就是一个普通的、有序的双向链表。RT_TIMER_SKIP_LIST_LEVEL
大于1,代码中跳跃表的功能就体现出来了,引用 SkipList的那点事儿 中的一张图。row_head[0]
更新为跳跃表最上层的第一个节点,内部的for循环的目的是找到当前这一级合适的节点,然后在第⑴处垂直下降到下一层,继续下一层的搜索。row_head[row_lvl]中存的是每一层所有比目标值小的节点中最大的节点,当外层的for循环退出后,row_head[row_lvl]之后就是整个跳跃表中第一级合适的插入位置,在第⑵处执行插入操作,此时row_lvl = RT_TIMER_SKIP_LIST_LEVEL - 1
。current_tick >= t->timeout_tick
时,说明该定时器超时了,然后调用调用超时函数,传入的参数是该线程的线程控制块地址,再根据定时器标志停止或重新启动该定时器。/**
* 该函数用于扫描系统定时器列表,当有超时事件发生时
* 就调用对应的超时函数
*
* @note 该函数在操作系统定时器中断中被调用
*/
void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
register rt_base_t level;
/* 获取系统时基计数器rt_tick的值 */
current_tick = rt_tick_get();
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 系统定时器列表不为空,则扫描定时器列表 */
while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
/* 获取第一个节点定时器的地址 */
t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, /* 节点地址 */
struct rt_timer, /* 节点所在的父结构的数据类型 */
row[RT_TIMER_SKIP_LIST_LEVEL - 1]); /* 节点在父结构的成员名 */
/* uint 如果 current_tick > t->timeout_tick, 说明该定时器超时了 */
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
/* 先将定时器从定时器列表移除 */
_rt_timer_remove(t);
/* 调用超时函数 */
t->timeout_func(t->parameter);
/* 重新获取 rt_tick */
current_tick = rt_tick_get();
/* 如果t是周期定时器,则重新启动 */
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* 启动定时器 */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
/* 单次定时器 */
else
{
/* 停止定时器 */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
}
else
break;
}
/* 开中断 */
rt_hw_interrupt_enable(level);
}
/**
* 线程超时函数
* 当线程延时到期或者等待的资源可用或者超时时,该函数会被调用
*
* @param parameter 超时函数的形参
*/
void rt_thread_timeout(void *parameter)
{
struct rt_thread *thread;
thread = (struct rt_thread *)parameter;
/* 设置错误码为超时 */
thread->error = -RT_ETIMEOUT;
/* 将线程从挂起列表中删除,本章节并没有使用挂起列表 */
rt_list_remove(&(thread->tlist));
/* 将线程插入到就绪列表 */
rt_schedule_insert_thread(thread);
/* 系统调度 */
rt_schedule();
}
/* 延时函数 */
rt_err_t rt_thread_delay(rt_tick_t tick)
{
return rt_thread_sleep(tick);
}
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
register rt_base_t temp;
struct rt_thread *thread;
/* 关中断 */
temp = rt_hw_interrupt_disable();
/* 获取当前线程的线程控制块 */
thread = rt_current_thread;
/* 挂起线程 */
rt_thread_suspend(thread);
/* 设置线程定时器的超时时间 */
rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
/* 启动定时器 */
rt_timer_start(&(thread->thread_timer));
/* 开中断 */
rt_hw_interrupt_enable(temp);
/* 执行系统调度 */
rt_schedule();
return RT_EOK;
}
void rt_tick_increase(void)
{
rt_tick ++;
rt_timer_check();
}
11_timer