平台: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值,若相等去执行任务---希望各位给不同的意见
转载于:https://blog.51cto.com/pregnant/1372017