在任何一个RTOS中,都免不了系统延时定时器
timer队列,在系统延时、等待事件等之时都是通过它触发任务切换,uc/osii和rtt中也不例外。但在uc/osii和rtt中都不约而同地选择了实现简单但效率极低的尾部添加模式,即一旦有任务调用了 delay系统功能,操作系统就把当前的任务资料以及需要延时的系统tick数追加到定时器队列的尾部,在定时器任务得到运行时,一个一个依次查询每个任务的延时是否已经到达,若已到则唤醒任务(但不会马上切换到该任务),然后再查询下一个任务的延时状态。如果正处于延时状态的任务数量比较多,那么这将是一个很耗时很低效的过程。个人觉得应该修改这个算法,在每一次增加一个定时器之时,把需要的延时ticks数与队列中已有定时器的延时数作比较,算出其差值,再将该任务信息插入到队列中合适的位置。举例为说,假如第一次一个任务要延时30,则队列中只有一个延时任务,延时数为30。第二次另一个任务要延时20(假如此时还没有运行过定时器任务),则把第二个任务排在第一位,原任务的延时数改为10(30-20=10,表示在第一个任务完成后还要延时10个ticks。如果还有第三个任务需要延时40,则40-20-10=10,它的延时数也修正为10,并追加到队列最后。因为这样的遍历只需要在增加一个延时任务之时才运行,而在定时器任务中,每次只需要检查第一个任务的延时是否已经到达(如果已经到达则需要检查它后面的任务是否也已到达),勿需再遍历队列中的每个任务,效率将得到明显提高。以伪语言描述如下:
修改前:
dalay(...)
{
add task to delay_list
delay_list.task.delay = dlay_time
task_schedule()
}
timer_task(...)
{
for(i = 0; i < MAX_TIMERS; i++)
{
if(delay_list.task is valid)
{
if(0 == (--delay_list.task.delay))
{
set task.status to active
delete task from delay_list
}
}
}
task_schedule()
}
修改后:
dalay(...)
{
pLast, pNext // 指向定时器队列的指针
pLast = First, pNext = pLast->Next
for(i = 0; i < MAX_TIMERS; i++)
{
if(pLast->task is valid)
{
if(delay_time > pLast->task->delay)
{
delay_time -= pLast->task->delay
}
else
{
this task->Next = pLast->task->Next // insert task to delay_list
pLast->task->Next = this task
this task->delay = delay_time
exit loop
}
task_schedule()
}
}
}
timer_task(...)
{
First->task->delay--
while(0 == First->task->delay)
{
set First ->task->status to active
First = First ->task->Next // delete task from delay_list
}
task_schedule()
}
First是指向系统定时器队列第一个元素的指针。
因为比较绕,我担心没有讲清楚,但愿通过伪语言有助于大家的理解。