注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
fwjieok的个人空间 https://home.eeworld.com.cn/space-uid-525366.html [收藏] [复制] [分享] [RSS]
日志

2. RAW-OS学习笔记之——时钟体系

已有 1702 次阅读2014-6-26 15:00

2. Raw-OS学习笔记之——时钟系统
内核版本基于1.052最新版,资料参照RAW-OS作者txj编写的《高效实时操作系统设计》教程和书本配套的视频以及源码视频讲解。
    Raw-OS官方网站:http://www.raw-os.org/
    Raw-OS托管地址:https://github.com/jorya/raw-os/
    笔记仅为个人理解,想深入学习RAW-OS 及RTOS原理,请购买raw-os作者录制的视频教程,淘宝链接:http://shop111107957.taobao.com/,为了自已能力的提高,同时也为支持RAW-OS作者。
总是说国外的XX-OSX,总是说国人没有自已的OS,支持国人,从RAW-OS开始。
1.raw_time_tick分析    RTOS的时钟节拍是系统运行的时基,对于RAW-OS来说,由于其引入了一些的创新机制,在硬件时钟ISR设计上并不是简单的调用系统节拍函数,而是要考虑两个方面,RAW-OS引入了Task_0,这个最高优先级的任务是为了配合RAW-OS最大关中断0us特性存在,所以在我的STM32F4 Discovery这个板子上编写的时钟节拍服务函数如下:voidSysTick_Handler(void)         // M3,M4的SsyTick定时器中断服务程序
{
    raw_enter_interrupt();           // 进入中断前必须调用
    // 如果开了最大关中断0特性,TASK_0也必须开,所以此时只能用task_0_tick_post来处理时钟节拍
#if(CONFIG_RAW_TASK_0 > 0)         
task_0_tick_post();
#else                              
// 如果没有开最大关中断0特性,则调用raw_time_tick()来更新处理时钟节拍
    raw_time_tick();
#endif
   
    raw_finish_int();                // 退出中断必须调用
}
TASK_0机制为RAW-OS创新引入的,其实现也比较复杂,这里先对关最大0中断特性时的处理方法作一个分析。
raw_time_tick()这个函数在raw_system.c中定义,其源码如下:
RAW_VOIDraw_time_tick(void)
{
    // 开了task_0,则必须用task_0_tick_post()来处理时钟节拍
    #if (CONFIG_RAW_TASK_0 > 0)         
   
    if (raw_int_nesting) {
        RAW_ASSERT(0);
            
    }
    #endif
   
    #if (CONFIG_RAW_USER_HOOK > 0)      // 自定义hook函数
    raw_tick_hook();
    #endif
    /*update system time to calculate whether task timeouthappens*/
    // 如果开了tick_task任务,则发出信号量,同步tick_task任务,把tick更新放到任务级,这里是最大限度的保证实时性(后面会分析)
#if (CONFIG_RAW_TICK_TASK > 0)         
   raw_task_semaphore_put(&tick_task_obj);
    #else                               // 否则直接在中断中更新ticK_list
    tick_list_update();
    #endif
    /*update task time slice if possible*/
    #if (CONFIG_SCHED_FIFO_RR > 0)      // 计算时间片
    calculate_time_slice(raw_task_active->priority);
    #endif
    /*inform the timer task to update software timer*/  
    #if (CONFIG_RAW_TIMER > 0)          // 开了定时器
    call_timer_task();
    #endif
}
系统时钟节拍的执行流程一般是这样的:

在这里详细讨论这个raw_enter_interrupt()和raw_finish_int()函数,前者是入中断ISR时首先调用,后者是退出中断ISR时最后调用,那么这两个函数做了什么工作,看看源码注释:
RAW_U16raw_enter_interrupt(void)      
{
    RAW_SR_ALLOC();
// 检查中断嵌套层数,超过默认阀值立即返回
    if (raw_int_nesting >=INT_NESTED_LEVEL) {         
        returnRAW_EXCEED_INT_NESTED_LEVEL;                                                                                    
    }
    // 关CPU中断,因为raw_int_nesting变量属于临界资源
RAW_CPU_DISABLE();      
raw_int_nesting++;       // 中断嵌套层数++,中断也可以被中断
RAW_CPU_ENABLE();      
    return RAW_SUCCESS;
}
Raw_finish_int()函数源码,退出中断时必须调用,在raw_system.c中定义:
RAW_VOIDraw_finish_int(void)
{
RAW_SR_ALLOC();             // 定义一个变量来存放cpu的状态寄存器
USER_CPU_INT_DISABLE();     // 保存cpu状态寄存器到上面定义的变量中
    // raw_finish_int必须与raw_int_enter()配套使用,raw_int_nesting为0说明已经处理完了中断,不能再调用,所以这里恢复cpu状态寄存器,并直接返回
    if (raw_int_nesting == 0) {
        USER_CPU_INT_ENABLE();                                 
        return;
    }
    // 中断嵌入层数-1
    raw_int_nesting--;
// 如果中断仍在嵌套中,则恢复cpu状态寄存器,返回
if (raw_int_nesting) {              
        USER_CPU_INT_ENABLE();                                 
        return;
    }
    // 关调度时直接返回
    if (raw_sched_lock) {
        USER_CPU_INT_ENABLE();        
        return;
    }
// 运行到这里说明是最后一层中断了,所以要在退出时执行任务调度
// 查找最高优先级任务
    get_ready_task(&raw_ready_queue);
    // 如果被中断的任务仍为最高优先级,则无需调度直接返回
    if (high_ready_obj ==raw_task_active) {                 
        USER_CPU_INT_ENABLE();                                    
        return;
    }
    // 系统调试相关
TRACE_INT_TASK_SWITCH(raw_task_active, high_ready_obj);
   
    // 如果有更调优先级任务就绪,则进行中断级任务调度,切换到最高优先级任务
    raw_int_switch();  
    USER_CPU_INT_ENABLE();  
}
对于上面函数中出现的宏,他们的定义如下,很好理解,不作解释。
#define  RAW_SR_ALLOC()                  unsigned int cpu_sr = 0
#define  USER_CPU_INT_DISABLE()         {cpu_sr = OS_CPU_SR_Save();}
#define  USER_CPU_INT_ENABLE()          {OS_CPU_SR_Restore(cpu_sr);}
对于raw_time_tick函数中的关键部分:
#if (CONFIG_RAW_TICK_TASK > 0)     // 将tick_list更新转到任务级   
    raw_task_semaphore_put(&tick_task_obj);
    #else                              
    tick_list_update();                 // 直接在中断中更新
    #endif
总的来说是这样的:如果开了最大关中断0特性,则必须开task_0机制,则时钟节拍的处理更新由task_0来处理(以后会分析原理),如果没有开最大关中断0特性,则还可以开tick_task任务,将时钟节拍的处理更新转到tick_task这个任务来处理,如果两个都没开,则在raw_time_tick中直接update tick list
总之就是,能不在中断中做的就转到任务级处理,最大保证系统的实时性,txj的这句话让我记忆深刻,raw-os最大关中断0us特性确实牛Xtask_0特性以后再慢慢研究。

2. tick_task(时钟节拍任务)分析
又引入了tick_task,这是一个任务,专门用于更新系统时钟节拍的任务,为什么要引入这个东西?还是那句话,能不在中断中做的都不在中断中做,为的是最大保证系统实时性,当然,ucos ii是没有这些东西的,他是直接更新tick list,这个机制的原理如下图所示:



这个tick_task_start()函数就是创建tick_task任务的,关于tick_task,相关源码如下:

#if(CONFIG_RAW_TICK_TASK > 0)   // 使能了tick_task// tick_task处理函数
static void tick_task_process(void *para)
{
    RAW_U16 ret;   
    while (1) {
        // 等待信号量。。。还记得raw_time_tick函数中的:raw_task_semaphore_put(&tick_task_obj)吗
        // 第时在时钟节拍ISR中,发送信号量来同步这个tick_task
        ret = raw_task_semaphore_get(RAW_WAIT_FOREVER);
      
       // 获得信号量成功且系统正在运行的性况下,更新tick_list_update
        if (ret == RAW_SUCCESS) {
            if (raw_os_active ==RAW_OS_RUNNING) {
                tick_list_update();
            }
        }      
    }
}
//tick_task_start函数用于tick_task的初始化,在raw_os_init中调用
void tick_task_start(void)
{
    // 创建tick_task任务,用于update tick list
    raw_task_create(&tick_task_obj, (RAW_U8  *)"tick_task_object",  0,
    TICK_TASK_PRIORITY,  0, tick_task_stack, TICK_TASK_STACK_SIZE, tick_task_process, 1);
    // 创建一个用于tick同步的信号量
    raw_task_semaphore_create(&tick_task_obj, &tick_semaphore_obj, (RAW_U8 *)"tick_semaphore_obj", 0);
}
#endif
基中对于tick_task相关的定义(栈,任务对像,同步信号量)是在raw_obj.c中定义:
#if(CONFIG_RAW_TICK_TASK > 0)
RAW_TASK_OBJ              tick_task_obj;
PORT_STACK                tick_task_stack[TICK_TASK_STACK_SIZE];
RAW_SEMAPHORE             tick_semaphore_obj;
#endif

额,好了,总算是弄完了,从这函数分析还可以知道raw_task_semaphore是这样用的,raw_task_semaphore_put(&tick_task_obj),直接把信号量发给了tick_task这个任务,在RAW-OS中提供了任务信号量这种机制,当时确的知道ISR和任务或任务和任务之间的同步关系时,可以直接把信号量发给这个任务。


3. tick_list_update

好像还漏了点什么,对,tick_list_update(),在raw_time_tick()中,关键就是对这个玩意的调用,看上面tick_task_process()的源码知道,tick_task的存在就是为了调用这个东西,再来分析一下它,不分析源码,只分析原理:系统内核维护了一个任务就绪队列,所有处于就绪态的任务都挂在这个就绪队列上,当进行任务调度的时候,会从这个队列中取出最高优先级的任务来运行,任务调度必然伴随着任务状态的切换(看上一篇学习笔记——任务篇),那么就涉及到任务对像在就绪队列的插入,删除等,tick_list_update()正是维护这个就绪队列,同时也更新系统,任务的一些状态标志,此过程比较复杂,自行看源码结合txj的视频讲解看,理解的更好。


另外,除了就绪队列外,tick_list_update()还更新了tick_list这个链表,这个链表上挂接的就是那需要等待系统时基来获得运行权的任务,具体还没看到这些个功能,后面再看,再分析。

总之来说,tick_list_update()就是遍历系统中中所有等待期满的任务、等待事件超时的任务,就绪的任务,然后发生一系列的转换,使系统有序的运转。




本文来自论坛,点击查看完整帖子内容。

全部作者的其他最新日志
评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章