2. Raw-OS学习笔记之——时钟系统
内核版本基于1.052最新版,资料参照RAW-OS作者txj编写的《高效实时操作系统设计》教程和书本配套的视频以及源码视频讲解。
总是说国外的XX-OS牛X,总是说国人没有自已的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特性确实牛X,task_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()就是遍历系统中中所有等待期满的任务、等待事件超时的任务,就绪的任务,然后发生一系列的转换,使系统有序的运转。
本文来自论坛,点击查看完整帖子内容。