一、步骤简介
1.创建任务0 , 任务0处理函数为task_0_process(void *pa);
2.设置时钟节拍中断时发给任务0的event的处理函数task_0_event_handler.handle_event= task_0_tick_handler(RAW_U8 ev, RAW_U8 *data);
3.把任务0的就绪标志置'1' , task_0_is_ready = 1 .
4.把任务0设置为当前最高优先级任务 ,并作为当前运行任务 .
5.启动os , 第一次进入时钟节拍中断, 调用task_0_tick_post()发送时钟节拍事件task_0_event_handler (在步骤2,对其赋值) .
6 .调用raw_finish_int()切换到任务0去运行;
7.task_0_process(void*pa)会获取时钟节拍事件并执行该事件的处理函数task_0_tick_handler(RAW_U8 ev, RAW_U8*data)
二、详细过程及源码分析
1.在raw_os_init()(定义在raw_sched.c里)函数里通过调用raw_task_0_init()(定义在raw_task_0.c里)对任务0进行初始化 .源码及注释如下:
void raw_task_0_init()
{
/*创建任务0 , 该任务0的处理函数指针为timer_0_process(后面会提起) , 并自动加入到就绪链表上*/
raw_task_create(&raw_task_0_obj, (RAW_U8 *)"timer_object", 0,
0, 0, task_0_stack, TASK_0_STACK_SIZE, task_0_process, 1);
/*设置任务0的event处理函数*/
task_0_event_handler.handle_event = task_0_tick_handler;
/*设置任务0就绪标志*/
task_0_is_ready = 1;
#if (CONFIG_RAW_ZERO_INTERRUPT > 0)
int_msg_init();
#endif
}
2.之后调用raw_os_start(),该函数启动rawos并开始执行第一个最高优先级的任务 . 也就是上面创建的任务0 ,
RAW_U16 raw_os_start()
{
if (raw_os_active == RAW_OS_STOPPED) {
/*获取最高优先级的任务 , 任务0控制块会被存在high_ready_obj*/
get_ready_task(&raw_ready_queue);
/*把最高就绪任务(任务0)作为当前运行任务*/
raw_task_active = high_ready_obj;
raw_os_active = RAW_OS_RUNNING;
/*
*这个函数的里面模拟中断处理simulated_interrupt_process()
*会调用到task_0_tick_post()(步骤3)来发送时钟节拍event给任务0,然后调用
*raw_finish_int()(步骤4)来作退出中断处理,主要完成了获取最高优先级任务,
*执行一次中断切换raw_int_switch()给最高优先级的任务 , 然后跳
*任务0的处理函数task_0_process()(步骤5)去执行
**/
raw_start_first_task();
}
else {
return RAW_OS_RUNNING;
}
#if (CONFIG_RAW_ASSERT > 0)
RAW_ASSERT(0);
#endif
return RAW_SYSTEM_ERROR;
}
3.一旦os开始了, 则在每一次的时钟节拍中断里面需要调用task_0_tick_post()来传递时钟节拍中断事件给任务0
该函数定义及注释如下:
/*
*发送时钟节拍中断的event给任务0
*触发任务0执行时钟节拍中断的后续处理
**/
void task_0_tick_post()
{
/*发送event给任务0 , */
raw_task_0_post(&task_0_event_handler, raw_task_active->priority, 0);
}
/*
*该函数用于给任务0发送event
**/
void raw_task_0_post(EVENT_HANLDER *p, RAW_U8 ev, RAW_U8 *data)
{
RAW_U32 snum;
RAW_SR_ALLOC();
/*if message is max, probally interrupt is too fast, please check your interrupt*/
if(nevents_points == MAX_TASK_EVENT) {
RAW_ASSERT(0);
}
/*fastest way to make task 0 ready*/
RAW_CPU_DISABLE();
/*fevent_point指向最先进入队列的任务, 计算出任务0的event队列的第一个空闲位置 , 用于插入event . (FIFO)*/
snum = (fevent_point + nevents_points) & (MAX_TASK_EVENT - 1); //
/*任务0 event数加一*/
++nevents_points;
/*设置任务0就绪标志*/
if (task_0_is_ready == 0) {
task_0_is_ready = 1;
}
RAW_CPU_ENABLE();
/*把该event插入队列里*/
task_0_events_queue[snum].ev = ev;
task_0_events_queue[snum].data = data;
task_0_events_queue[snum].p = p;
/*更新调试信息*/
if (nevents_points > peak_events) {
peak_events = nevents_points;
}
}
4.调用raw_finish_int()来处理退出中断的必要工作并退出中断 .
函数定义及注释如下:
RAW_VOID raw_finish_int()
{
RAW_SR_ALLOC();
RAW_CPU_DISABLE();
/*如果已经不在中断里面的 , 则直接返回*/
if (raw_int_nesting == 0) {
RAW_CPU_ENABLE();
return;
}
/*把中断嵌套数减一*/
raw_int_nesting--;
/*如果减一之后还处在中断里面 , 也直接返回,因为中断里面不允许调度 */
if (raw_int_nesting) {
RAW_CPU_ENABLE();
return;
}
/*如果调度器被上锁了,也不能切换给高优先级的任务 , 直到调度锁被释放*/
if (raw_sched_lock) {
RAW_CPU_ENABLE();
/*if interrupt happen here, it may cause raw_int_nesting equal 0*/
return;
}
#if (CONFIG_RAW_TASK_0 > 0)
/*如果任务0处于就绪态 , 则需要把任务0交给high_ready_obj*/
if (task_0_is_ready) {
high_ready_obj = &raw_task_0_obj;
}
/*否则,需要在就绪队列里获取最高优先级的任务*/
else {
/*get the highest task*/
get_ready_task(&raw_ready_queue);
}
#else
/*get the highest task*/
get_ready_task(&raw_ready_queue);
#endif
/*如果当前被中断的任务已经是最高优先级的任务了,就不需要执行下面的切换了,直接退出*/
if (high_ready_obj == raw_task_active) {
RAW_CPU_ENABLE();
return;
}
/*切换到最高优先级的任务(此时是任务0,所以会切换到task_0_process()(步骤5))去执行 , 这个函数涉及到CPU硬件的处理 , 需要被移植*/
raw_int_switch();
RAW_CPU_ENABLE();
}
5.task_0_process()(定义在raw_task_0.c里 , 任务0的处理函数) ,
该函数源码及注释如下:
static void task_0_process(void *pa)
{
RAW_U8 ev;
RAW_U8 *data;
EVENT_HANLDER *receiver;
RAW_U8 done;
RAW_S32 i;
RAW_SR_ALLOC();
/*把任务0控制块从就绪链表里摘除*/
list_delete(&raw_task_0_obj.task_list);
/*把就绪队列里task_bit_map对应于任务0的位清零*/
data = (RAW_U8 *)raw_ready_queue.task_bit_map;
*data &= ~1;
while (1) {
done = 0;
while (done == 0) {
/*以下获取消息并更新*/
RAW_CPU_DISABLE();
if (nevents_points) {//不为0表示有event发生,遍历直到所有event处理完
/* 任务0释放这些event */
/*
*fevent_point指明发生的event所存放在task_0_events_queue的位置,
*取出这些event信息进行处理
**/
ev = task_0_events_queue[fevent_point].ev;
data = task_0_events_queue[fevent_point].data;
receiver = task_0_events_queue[fevent_point].p;
/*更新fevent_point , 对其加一指向下一个事件,如果达到数组尾部则跳到数组头部继续取*/
fevent_point = (fevent_point + 1) & (MAX_TASK_EVENT - 1);
/*更新nevents_points , 因为取出了一个event了,所以对其减一*/
--nevents_points;
RAW_CPU_ENABLE();
/*
*执行该事件的关联函数 , handle_event由用户自定义 ,
*此例为步骤3 post的task_0_event_handler->hendle_event,
*在步骤1有对其赋值为task_0_tick_handler ,
* 所以调用步骤6的函数
**/
receiver->handle_event(ev, data);
}
/*指明没有event需要处理了,执行以下代码*/
else {
/*关闭任务0就绪标志*/
task_0_is_ready = 0;
RAW_CRITICAL_ENTER_CPU_ENABLE();
/*获取下一个最高优先级与当前优先级与优先级1的偏移量*/
i = bit_search_first_one(raw_ready_queue.task_bit_map, 1, CONFIG_RAW_PRIO_MAX - 1);
/*更新就绪队列里维护的最高优先级*/
if (i >= 0) {
raw_ready_queue.highest_priority = 1 + i;
}
else {
#if (CONFIG_RAW_ASSERT > 0)
RAW_ASSERT(0);
#endif
}
/*这里退出临界区而不执行调度*/
RAW_CRITICAL_EXIT_NO_SCHED();
/*从任务0切换到其它普通任务去*/
task_0_switch(); //从任务0切换到普通任务执行 ,切换之后任务0不再被调度 , 直到task_0_is_ready=1
done = 1;
}
}
}
}
6.调用task_0_tick_handler(RAW_U8 ev, RAW_U8 *data)用于处理时钟节拍中断的后续事情(中断下半部) .
函数定义及注释如下:
void task_0_tick_handler(RAW_U8 ev, RAW_U8 *data)
{
data = data;
#if (CONFIG_RAW_USER_HOOK > 0)
raw_tick_hook();
#endif
/* 更新系统的时钟节拍计数 , 判断是否有任务阻塞超时或者睡眠超时,不会开启调度器*/
tick_list_update();
/*update task time slice if possible*/
#if (CONFIG_SCHED_FIFO_RR > 0)
/*更新指定优先级第一个任务的时间片 , 通常传入当前优先级 ,
*如果时间片用完就要把该任务放到该链表的尾部并复位时间片
*在每一次调用RAW_CRITICAL_EXIT()退出临界区都会判断任务0
*是否就绪了,是话会调度到任务0执行*/
caculate_time_slice(ev);
#endif
#if (CONFIG_RAW_TIMER > 0)
/*通知软定时器管理任务更新软定时器*/
call_timer_task();
#endif
}