RAW-OS:时间管理

                         平台: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值,若相等去执行任务---希望各位给不同的意见

你可能感兴趣的:(时间管理,RAW-OS)