平台:VS2010 版本:1.04
在讲解任务管理之前先看RAW-OS的就绪队列结构:
typedef struct RAW_RUN_QUEUE { RAW_U8 highest_priority; LIST task_ready_list[CONFIG_RAW_PRIO_MAX]; RAW_U32 task_bit_map[NUM_WORDS]; } RAW_RUN_QUEUE;
先看看RAW_RUN_QUEUE这个结构体:highest_priority指当前就绪任务的最高优先级(RAW-OS会执行优先级为highest_priority的任务);task_ready_list[CONFIG_RAW_PRIO_MAX]是就绪任务的链表,CONFIG_RAW_PRIO_MAX代表可以有多少个优先级,每个优先级都有自己的就绪任务链表;若task_bit_map(n)为1代表有优先级为n的就绪任务(n/32为task_bit_map数组的index)。这样任务调度时,RAW-OS就可以根据RAW_RUN_QUEUE来进行任务切换。
任务的管理重点就是任务的调度,那什么时候会产生任务的调度呢。
一:RAW-OS的任务状态改变(来自RAW-OS作者的任务管理文档):
由上图我们看到RAW-OS有这么多状态,但是我们根据状态名不难发现改变任务状态的事件:sleep(DLY)、阻塞(PEND或者PEND_TIMEOUT)、暂停(SUPEND)和resume(只要状态中有SUPEND,resume就可以让任务去掉SUPEND状态)。那我们就分析下各个事件操作
1.1 sleep
RAW_U16 raw_sleep(RAW_TICK_TYPE dly) { RAW_U16 error_status; RAW_SR_ALLOC(); #if (RAW_TASK_FUNCTION_CHECK > 0) if (raw_int_nesting) { return RAW_NOT_CALLED_BY_ISR; } #endif RAW_CRITICAL_ENTER(); if (dly) { /*system is locked so task can not sleep just return immediately*/ SYSTEM_LOCK_PROCESS(); raw_task_active->task_state = RAW_DLY; tick_list_insert(raw_task_active, dly); remove_ready_list(&raw_ready_queue, raw_task_active); } else { /*make current task to the end of ready list*/ move_to_ready_list_end(&raw_ready_queue, raw_task_active); } RAW_CRITICAL_EXIT(); raw_sched(); if (dly) { /*task is timeout after sleep*/ error_status = block_state_post_process(raw_task_active, 0); } else { error_status = RAW_SUCCESS; } return error_status; }
sleep函数参数dly指task的sleep时间,单位是1个时间节拍(以下若涉及到时间都是以1个时间节拍为单位)。sleep的具体操作如下:若dly>0,将task的state改为DLY并插入到tick_list链表中(参考时间管理),接着在就绪队列中删除当前task;dly=0,将task移到就绪队列的最后面(此时task的state没改变还是RDY)。当过去dly后(tick_list_updat函数),task的state就变成RDY了并重新加入到就绪队列中。
1.2 阻塞--信号量阻塞
RAW_U16 raw_pend_object(RAW_COMMON_BLOCK_OBJECT *block_common_obj, RAW_TASK_OBJ *task_ptr, RAW_TICK_TYPE timeout) { /*timeout 0 should not happen here, it has been processed before*/ if (timeout == 0u) { RAW_ASSERT(0); } /*task need to remember which object is blocked on*/ task_ptr->block_obj = block_common_obj; if (timeout == RAW_WAIT_FOREVER) { task_ptr->task_state = RAW_PEND; } /*task is blocked with timeout*/ else { /*add to time sorted tick list */ tick_list_insert(task_ptr,timeout); task_ptr->task_state = RAW_PEND_TIMEOUT; } /*Remove from the ready list*/ remove_ready_list(&raw_ready_queue, task_ptr); //将task_list挂到等待信号量的链表下 if (block_common_obj->block_way == RAW_BLOCKED_WAY_FIFO) { /*add to the end of blocked objet list*/ list_insert(&block_common_obj->block_list, &task_ptr->task_list); } else { /*add to the priority sorted block list*/ add_to_priority_list(&block_common_obj->block_list, task_ptr); } return RAW_SUCCESS; }
信号量阻塞事件将task从就绪队列中删除并将之添加到block_list(信号量)中。根据阻塞函数的timeout函数,分为以下两种情况:若timeout=RAW_WAIT_FOREVER,则表示task会一直等下去即state=RAW_PEND直到获取到信号量state才会变成RDY;相反state=RAW_PEND_TIMEOUT,并根据timeout将task添加到tick_list链表(等待timeout(tick_list_updat)后,task自动变成RDY状态并添加到就绪队列),但是如果在timeout之前获取到信号量,此时state也会变为RDY。
1.3 SUPEND
RAW_U16 task_suspend(RAW_TASK_OBJ *task_ptr) { RAW_SR_ALLOC(); RAW_CRITICAL_ENTER(); if (task_ptr == raw_task_active) { SYSTEM_LOCK_PROCESS(); } switch (task_ptr->task_state) { case RAW_RDY: task_ptr->suspend_count = 1; task_ptr->task_state = RAW_SUSPENDED; remove_ready_list(&raw_ready_queue, task_ptr); break; case RAW_DLY: task_ptr->suspend_count = 1; task_ptr->task_state = RAW_DLY_SUSPENDED; break; case RAW_PEND: task_ptr->suspend_count = 1; task_ptr->task_state = RAW_PEND_SUSPENDED; break; case RAW_PEND_TIMEOUT: task_ptr->suspend_count = 1; task_ptr->task_state = RAW_PEND_TIMEOUT_SUSPENDED; break; case RAW_SUSPENDED: case RAW_DLY_SUSPENDED: case RAW_PEND_SUSPENDED: case RAW_PEND_TIMEOUT_SUSPENDED: task_ptr->suspend_count++; if (task_ptr->suspend_count >= 250u) { RAW_CRITICAL_EXIT(); return RAW_SUSPENDED_COUNT_OVERFLOWED; } break; default: RAW_CRITICAL_EXIT(); return RAW_STATE_UNKNOWN; } RAW_CRITICAL_EXIT(); TRACE_TASK_SUSPEND(raw_task_active, task_ptr); #if (CONFIG_RAW_SYSTEM_PREEMPTABLE > 0) raw_sched(); return RAW_SUCCESS; }
supend将state追加SUPEND状态,若原本state为RDY,则需要将task从就绪队列中删除并将state修改为supend。当之前的state符合时,state为SUPEND,例当state为pend_supend时获取到信号量,state就变为supend。
1.4 resume
RAW_U16 task_resume(RAW_TASK_OBJ *task_ptr) { RAW_SR_ALLOC(); RAW_CRITICAL_ENTER(); switch (task_ptr->task_state) { case RAW_RDY: case RAW_DLY: case RAW_PEND: case RAW_PEND_TIMEOUT: RAW_CRITICAL_EXIT(); return HAS_NOT_SUSPENDED; case RAW_SUSPENDED: task_ptr->suspend_count--; if (task_ptr->suspend_count == 0) { /*Make task ready*/ task_ptr->task_state = RAW_RDY; add_ready_list(&raw_ready_queue, task_ptr);//如果同当前任务的优先级相同则插到队列的最后,否则插到队列的最前面 } break; case RAW_DLY_SUSPENDED: task_ptr->suspend_count--; if (task_ptr->suspend_count == 0) { task_ptr->task_state = RAW_DLY; } break; case RAW_PEND_SUSPENDED: task_ptr->suspend_count--; if (task_ptr->suspend_count == 0) { task_ptr->task_state = RAW_PEND; } break; case RAW_PEND_TIMEOUT_SUSPENDED: task_ptr->suspend_count--; if (task_ptr->suspend_count == 0) { task_ptr->task_state = RAW_PEND_TIMEOUT; } break; default: RAW_CRITICAL_EXIT(); return RAW_STATE_UNKNOWN; } RAW_CRITICAL_EXIT(); TRACE_TASK_RESUME(raw_task_active, task_ptr); do_possible_sche(); return RAW_SUCCESS; }
看代码可知,resume能将有supend状态的task去掉supen状态。若task只是supend状态,需要将state改为RDY并将其添加到就绪队列(若state=RDY,则RAW-OS就会task添加到就绪队列中)。
二:任务状态的改变会引起schde,下面这种情况也会sched:
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(); /*FIFO 调度模式会一直执行到其他任务抢占或者是当前任务阻塞,跟task's timeslice无关*/ 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(); /*若当前任务列表只有一个任务那自然不需要计算timeslice*/ 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(FIFO模式例外),当任务执行time_slice后,RAW-OS就会将task移到就绪队列的末端(毫无疑问此时会sched)。
Sched:
void raw_sched(void) { RAW_SR_ALLOC(); /*if it is in interrupt or system is locked, just return*/ if (raw_int_nesting || raw_sched_lock) { return; } RAW_CPU_DISABLE(); get_ready_task(&raw_ready_queue); /*if highest task is currently task, then no need to do switch and just return*/ if (high_ready_obj == raw_task_active) { RAW_CPU_ENABLE(); return; } TRACE_TASK_SWITCH(raw_task_active, high_ready_obj); CONTEXT_SWITCH(); //设置任务切换标志并增加timer_or_task_switch_event信号量使之响应WaitForMultipleObjects函数后停止当前任务开启最高优先级的任务 RAW_CPU_ENABLE(); }
sched函数很简单,它要做的就是执行当前优先级最高的task。那RAW-OS怎么得到最高优先级task呢,其实看RAW_RUN_QUEUE结构体就一目了然。如果当前task是最高优先级的任务,则不需要进行任务的切换(Note1);否则就要进行任务的切换了,暂停当前任务(此时state还是为RDY)执行执行最高优先级的任务(关于切换的具体机制,可以查看WaitForMultipleObjects函数)。
Note1:任务状态的改变不一定就会进行任务的调度(只有从RDY到别状态才一定会任务调度),例如在task1的状态从PEND-->RDY时,如果task1的优先级不是最高的就不进行任务切换,所以RAW-OS在这里调用do_possible_sche()(#define do_possible_sche() raw_sched())。我想RAW-OS的意思已经表达的很清楚了。