平台:VS2010 版本:1.04
在学习RAW-OS的时间管理之前,请先了解WaitForMultipleObjects和semaphore原理。
先讲解"时间节拍"(系统心跳)这个概念,RTOS需要它来提供task延迟和等待超时等时间管理的最小时间单位。时间节拍的频率越高,系统的负荷越大(这个很好理解:频率越高占用mcu的时间就多)。这里我们的时间节拍是10ms,RAW-OS通过main函数中的start_vc_timer(开启每隔10ms触发的事件)设置。
RAW-OS中关于时间管理涉及到task的sleep、wait和time_slice。在这里不得不提到一个函数:
static void task_0_tick_handler(TASK_0_EVENT_TYPE ev, void *event_data) //有关时间方面的函数,没10ms会调用一次 { event_data = event_data; #if (CONFIG_RAW_USER_HOOK > 0) raw_tick_hook(); #endif /*update system time to calculate whether task timeout happens*/ #if (CONFIG_RAW_TICK_TASK > 0) raw_task_semaphore_put(&tick_task_obj);//释放tick_semaphore_obj唤醒tick_task去调用tick_list_update #else tick_list_update(); #endif /*update task time slice if possible*/ #if (CONFIG_SCHED_FIFO_RR > 0) calculate_time_slice(ev); #endif /*inform the timer task to update software timer*/ #if (CONFIG_RAW_TIMER > 0) call_timer_task(); #endif }
每隔时间节拍就会去调用task_0_tick_handler函数使实时性得到保证(在VS下具体的实现机制请看Note1,由于在每个硬件平台下实现机制是不同的这里略过)。下面讲解task_0_tick_handler函数体
一:raw_task_semaphore_put(&tick_task_obj)--sleep和等待事件
看代码可知,这里释放了tick_semaphore_obj信号量。tick_semaphore_obj看名字就知道是关于tick的一个信号量,它涉及到的tick_list_update。tick_list_update会根据当前的raw_tick_count(每过一个时间节拍就加1)值和tick_head将到时间(sleep苏醒或者是等待超时)task重新添加到各自的就绪队列。实现机制可看下面源代码:
tick_list_insert:将task插入到tick_list链表中
void tick_list_insert(RAW_TASK_OBJ *task_ptr, RAW_TICK_TYPE time) { LIST *tick_head_ptr; RAW_U16 spoke; if (time) { task_ptr->tick_match = raw_tick_count + time; task_ptr->tick_remain = time; spoke = (RAW_U16)(task_ptr->tick_match & (TICK_HEAD_ARRAY - 1) ); tick_head_ptr = &tick_head[spoke]; tick_list_priority_insert(tick_head_ptr, task_ptr);//根据tick_remain从小到大组成链表-->即按时间间隔的长短排列 task_ptr->tick_head = tick_head_ptr; } }
有8条相同的tick_list(0--7),根据tick_match模8的值(0--7)插入到对应的tick_list,且tick_list中的排列顺序是按照距离到期时间长短来排列的,也许表意不明看下面代码:
RAW_INLINE void tick_list_priority_insert(LIST *head, RAW_TASK_OBJ *task_ptr) { RAW_TICK_TYPE val; LIST *q, *list_start, *list_end; RAW_TASK_OBJ *task_iter_temp; list_start = list_end = head; val = task_ptr->tick_remain; for (q = list_start->next; q != list_end; q = q->next) { task_iter_temp = list_entry(q, RAW_TASK_OBJ, tick_list); /*sorted by remain time*/ if ((task_iter_temp->tick_match - raw_tick_count) > val) { break; } } list_insert(q, &task_ptr->tick_list); }
tick_list_update:将到期的task转为就绪状态
void tick_list_update(void) { LIST *tick_head_ptr; RAW_TASK_OBJ *p_tcb; LIST *iter; LIST *iter_temp; RAW_U16 spoke; RAW_SR_ALLOC(); RAW_CRITICAL_ENTER(); raw_tick_count++; spoke = (RAW_U16)(raw_tick_count & (TICK_HEAD_ARRAY - 1) ); tick_head_ptr = &tick_head[spoke]; iter = tick_head_ptr->next; while (RAW_TRUE) { /*search all the time list if possible*/ if (iter != tick_head_ptr) { iter_temp = iter->next; p_tcb = list_entry(iter, RAW_TASK_OBJ, tick_list); /*Since time list is sorted by remain time, so just campare the absolute time*/ if (raw_tick_count == p_tcb->tick_match) { //延迟时间到 switch (p_tcb->task_state) { //根据task_state进行相应的操作 } iter = iter_temp; } /*if current task time out absolute time is not equal current system time, just break because timer list is sorted*/ else { break; //这里要结合插入队列时操作一起分析,因为插入时是按照到期时间长短排列,若当前tick_list的最小remain都不相等,则后面肯定是不同的 } }//if (iter != tick_head_ptr) /*finish all the time list search */ else { break; } }//while (RAW_TRUE) RAW_CRITICAL_EXIT(); }
由于tick_list_update里switch...case语句过长且与主题关系不大就省略了。在tick_list_updat就是将到期的task从tick_list链表中释放掉并添加到就绪队列。值得一题的是为什么else里可以是break呢?这需要联系tick_list_priority_insert函数,tick_list排序是按照remain的,那按照match可以吗?这里还是有巧妙之处的。
二:calculate_time_slice(ev)---任务的time_slice
void calculate_time_slice(RAW_U8 task_prio) { RAW_TASK_OBJ *task_ptr; LIST *head; RAW_SR_ALLOC(); head = &raw_ready_queue.task_ready_list[task_prio]; RAW_CRITICAL_ENTER(); /*if ready list is empty then just return because nothing is to be caculated*/ if (is_list_empty(head)) { RAW_CRITICAL_EXIT(); return; } /*Always look at the first task on the ready list*/ /*because active task is the first task on the ready list*/ task_ptr = list_entry(head->next, RAW_TASK_OBJ, task_list); /*SCHED_FIFO does not has timeslice, just return*/ if (task_ptr->sched_way == SCHED_FIFO) { RAW_CRITICAL_EXIT(); return; } /*there is only one task on this ready list, so do not need to caculate time slice*/ /*idle task must satisfy this condition*/ if (head->next->next == head) { RAW_CRITICAL_EXIT(); return; } if (task_ptr->time_slice) { task_ptr->time_slice--; } /*if current active task has time_slice, just return*/ if (task_ptr->time_slice) { RAW_CRITICAL_EXIT(); return; } /*Move current active task to the end of ready list for the same priority*/ move_to_ready_list_end(&raw_ready_queue, task_ptr); /*restore the task time slice*/ task_ptr->time_slice = task_ptr->time_total; RAW_CRITICAL_EXIT(); }
RAW-OS在分配任务时会初始化任务的time_slice来管理任务的运行时间(RR模式,Note3)。即calculate_time_slice任务就是监视着当前运行任务的time_slice,当运行时间到期时就将该任务调整到任务队列的最后面(必然会进行任务的调度)。但是在calculate_time_slice中time_slice对以下几种情况无效的:
1 当前任务队列没任务
2 当前任务的调度模式时FIFO
3 当前任务队列只有一个任务
1、2两种情况不解释,上面说了若time_slice到期就将任务调整到任务队列的最后面。之后,调度器会启动任务队列的第一个任务(若无中断)。那如果任务队列只有一个任务呢???其实这跟task1.sleep(0)事件是相同的,当然条件是task1的任务队列只有一个任务。
三:call_timer_task--software Timer
void call_timer_task(void) { raw_timer_ctrl--; if (raw_timer_ctrl == 0u) { /*reload timer frequency ctrl.*/ raw_timer_ctrl = RAW_TIMER_RATE; /*Release a semphore to timer task*/ raw_semaphore_put(&timer_sem); } }
粗看之下call_timer_task似乎与软件定时器没有什么关系。但是注意看if语句里的代码: RAW_TIMER_RATE代表着定时器精度(参考Note4,也就是说1 software time=RAW_TIMER_RATE*时间节拍)定义了单位software time的时间长度;raw_semaphore_put(&timer_sem)释放了timer_sem信号量(关于timer_sem就不细说,因为你会发现software Timer和tick的实现机制一样的,看下面代码):
void timer_task(void *pa) { RAW_U16 position; LIST *timer_head_ptr; LIST *iter; LIST *iter_temp; RAW_TIMER *timer_ptr; /*reset the timer_sem count since it may not be 0 at this point, make it start here*/ raw_semaphore_set(&timer_sem, 0); pa = pa; while (1) { /*timer task will be blocked after call this function*/ raw_semaphore_get(&timer_sem, RAW_WAIT_FOREVER); raw_disable_sche(); /*calculate which timer_head*/ raw_timer_count++; position = (RAW_U16)(raw_timer_count & (TIMER_HEAD_NUMBERS - 1) ); timer_head_ptr = &timer_head[position]; iter = timer_head_ptr->next; while (RAW_TRUE) { /*if timer exits*/ if (iter != timer_head_ptr) { /*Must use iter_temp because iter may be remove later.*/ iter_temp = iter->next; timer_ptr = list_entry(iter, RAW_TIMER, timer_list); /*if timeout*/ if (raw_timer_count == timer_ptr->match) { /*remove from timer list*/ timer_list_remove(timer_ptr); /*if timer is reschedulable*/ if (timer_ptr->reschedule_ticks) { /*Sort by remain time*/ timer_ptr->remain = timer_ptr->reschedule_ticks; timer_ptr->match = raw_timer_count + timer_ptr->remain; position = (RAW_U16)(timer_ptr->match & (TIMER_HEAD_NUMBERS - 1)); timer_ptr->to_head = &timer_head[position]; timer_list_priority_insert(&timer_head[position], timer_ptr); } else { timer_ptr->timer_state = TIMER_DEACTIVE; } /*Any way both condition need to call registered timer function*/ /*the registered timer function should not touch any timer related API,otherwise you get deadlock*/ if (timer_ptr->raw_timeout_function) { timer_ptr->raw_timeout_function(timer_ptr->raw_timeout_param); } iter = iter_temp; } //if (raw_timer_count == timer_ptr->match) { else { break; } }//if (iter != timer_head_ptr) { /*exit because timer is not exit*/ else { break; } }//while (RAW_TRUE) { raw_enable_sche(); } }
timer_task任务里做的事相当于tick_list_update函数,也是将到期的software Timer从timer_list中删除。我就不对timer_task任务做过多的解释了。需要注意的是timer_task要执行software Timer的回调函数,并根据timer_ptr->reschedule_ticks(reschedule_ticks不为0 表示周期性的定时器触发回调函数,
周期为reschedule_ticks个单位software time --参考Note4)判断是否需要重新将此software Timer添加到timer_list中。
四:raw_tick_hook
RAW_VOID raw_tick_hook() { simulated_fun_count++; if (simulated_fun_count == 5) { simulated_fun_count = 0; if (simulated_fun) { simulated_fun(); //函数只在interrupt_test.c文件中开启 } } tick_count_simulate++; if (tick_count_simulate == 20) { tick_count_simulate = 0; if (event_fun) { event_fun(); } } Clk_SignalClk(); proto_tick_handler(); }
raw_tick_hook包含着很多的子函数看起来很复杂,但是你看函数名我们大概知道是基于tick即事件节拍去执行我们的程序。怎么说呢,像Clk_SignalClk()这个函数只是基于时间节拍产生秒=100*时间节拍(你可以利用秒去做RTC和万年历等任何基于秒操作的事件,就像时间节拍供Timer和tick)。如果你写过裸机的定时器中断代码(Note5)很容易就理解raw_tick_hook函数了,两个东西是一样的。那你想想一个事件分别由raw_tick_hook和software Timer实现有什么区别?软件定时器的回调函数是关了系统抢占运行的(Note6)。
终上所述,我们看到时间管理涉及到的是task的tick、time_slice和software Timer。其实在RAW-OS中是开启了两个任务来进行时间管理的:tick_task_process(tick_list_update)和timer_task。注意tick_task_process的优先级是1,timer_task优先级是5。
Note1:理解WaitForMultipleObjects和semaphore原理
NOte2:理解RAW-OS的任务就绪队列结构--RAW_RUN_QUEUE
Note3:任务调度模式:RR和FIFO
Note4:可参考官方文档:软件定时器(在高效实时操作系统原理及实践的压缩包下)
Note5:假设定时器中断间隔是10ms,在中断设定间隔1*10ms后置位扫描按键flag,在20*10ms后置位刷新液晶flag。那么扫描按键和刷新液晶就相当于raw_tick_hook里的子函数(当然在raw_tick_hook里只是释放信号量)了。
Note6:关于raw_tick_hook和software Timer,主要考虑从时间性能分析。当然有死循环和任务操作时间长只能是raw_tick_hook。先看看两者的机制:raw_tick_hook是count++,count=设定值执行任务;software Timer是对比timer_list中node的match值,若相等去执行任务---希望各位给不同的意见