第 15 章 软件定时器
软件定时器的分类
按照定时器设定方式分:
- 单次触发定时器:创建后只会触发一次定时通知事件,触发后该定时器自动停止(销毁)。
- 周期触发定时器:创建后按照设定的周期无限循环触发定时器通知事件,直到用户手动停止。
按照定时器超时后执行处理函数的上下文环境分:
- 超时函数运行在中断上下文环境中,要求执行函数的执行时间尽可能短,不可以执行等待其它事件等可能导致中断控制路径挂起的操作。优点是响应较迅速,实时性较高。
- 超时函数运行在任务上下文环境中,即创建一个任务来执行这个 函数,函数中可以等待或者挂起,但实时性较差。
软件定时器的设计和实现
软件定时器的数据结构设计:
/* software timer */
struct timer {
void (*func)(void *arg);
void *arg;
uint32_t timeout_tick;
};
extern struct timer *timer_create(void (*handler)(void *arg), void *arg, uint32_t timeout);
extern void timer_delete(struct timer *timer);
#define MAX_TIMER 10
static struct timer timer_list[MAX_TIMER];
注意:timer_create函数中timeout参数的单位是_tick(即软件定时器的时间设定都是tick的整数倍)
软件定时器的创建和删除
1、通过遍历timer_list数组,找到一个当前可用的软件定时器
2、对其进行初始化,把处理函数、指针参数、定时的时间进行赋值。返回当前软件定时器的指针。
3、删除软件定时器时,遍历timer_list数组,找到要删除的软件定时器,将其元素进行清除。
struct timer *timer_create(void (*handler)(void *arg), void *arg, uint32_t timeout)
{
/* TBD: params should be checked more, but now we just simplify this */
if (NULL == handler || 0 == timeout) {
return NULL;
}
/* use lock to protect the shared timer_list between multiple tasks */
spin_lock();
struct timer *t = &(timer_list[0]);
for (int i = 0; i < MAX_TIMER; i++) {
if (NULL == t->func) {
break;
}
t++;
}
if (NULL != t->func) {
spin_unlock();
return NULL;
}
t->func = handler;
t->arg = arg;
t->timeout_tick = _tick + timeout;
spin_unlock();
return t;
}
void timer_delete(struct timer *timer)
{
spin_lock();
struct timer *t = &(timer_list[0]);
for (int i = 0; i < MAX_TIMER; i++) {
if (t == timer) {
t->func = NULL;
t->arg = NULL;
break;
}
t++;
}
spin_unlock();
}
软件定时器的实现、处理流程:
1、发生定时器中断后,跳转到timer_handler函数
2、timer_handler调用timer_check函数,查询是哪个定时器生效了。
3、timer_check函数遍历timer_list,找到当前tick >= t->timeout_tick,调用该定时器的处理函数,并将参数一并传入,处理完后清除该定时器的设定。并更新硬件定时器的时间。(可以看出软件定时器是依托于硬件定时器发生的)
reg_t trap_handler(reg_t epc, reg_t cause)
{
reg_t return_pc = epc;
reg_t cause_code = cause & 0xfff;
if (cause & 0x80000000) {
/* Asynchronous trap - interrupt */
switch (cause_code) {
case 7:
uart_puts("timer interruption!\n");
timer_handler();
break;
}
}
void timer_handler()
{
_tick++;
printf("tick: %d\n", _tick);
timer_check();
timer_load(TIMER_INTERVAL);
schedule();
}
static inline void timer_check()
{
struct timer *t = &(timer_list[0]);
for (int i = 0; i < MAX_TIMER; i++) {
if (NULL != t->func) {
if (_tick >= t->timeout_tick) {
t->func(t->arg);
/* once time, just delete it after timeout */
t->func = NULL;
t->arg = NULL;
break;
}
}
t++;
}
}
软件定时器的优化
上述设计采用数组的方式,对于内存的使用和遍历的时间复杂度都有很大优化空间
对于内存的使用,我们可以采用链表的方式来动态分配,这样更加灵活。
对于遍历的时间复杂度,第一我们可以让定时器按照超时时间进行排序(因为系统时间tick总是从小到大);第二我们可以采用跳表的管理方式,牺牲一部分内存来换取时间。
如图所示,要寻找超时时间为20的定时器,采用二级跳表只需要类似于二分查找法的时间复杂度,大大降低了查找的时间复杂度。