前面说了一下任务的数据结构,就算是为了本文做个铺垫,现在我来说一下MQX是如何创建一个任务的。
MQX创建任务有多种方式,但创建任务的前提是必须有task_template_struct。这个task_template_struct可以通过在MQX_template_list里静态添加,也可以在其他地方添加(不建议这么做,原因后面会提)。
举个静态添加的例子,在hello.c中:
-
const TASK_TEMPLATE_STRUCT MQX_template_list[] =
-
{
-
/* Task Index, Function, Stack, Priority, Name, Attributes, Param, Time Slice */
-
{ WORLD_TASK, world_task, 1000, 9, "world", MQX_AUTO_START_TASK, 0, 0 },
-
{ HELLO_TASK, hello_task, 1000, 8, "hello", 0, 0, 0 },
-
{ 0 }
-
}
这个MQX_template_list在这里出现了:
-
const MQX_INITIALIZATION_STRUCT MQX_init_struct =
-
{
-
/* PROCESSOR_NUMBER */ BSP_DEFAULT_PROCESSOR_NUMBER,
-
/* START_OF_KERNEL_MEMORY */ BSP_DEFAULT_START_OF_KERNEL_MEMORY,
-
/* END_OF_KERNEL_MEMORY */ BSP_DEFAULT_END_OF_KERNEL_MEMORY,
-
/* INTERRUPT_STACK_SIZE */ BSP_DEFAULT_INTERRUPT_STACK_SIZE,
-
/* TASK_TEMPLATE_LIST */ MQX_template_list,
-
/* MQX_HARDWARE_INTERRUPT_LEVEL_MAX*/ BSP_DEFAULT_MQX_HARDWARE_INTERRUPT_LEVEL_MAX,
-
/* MAX_MSGPOOLS */ BSP_DEFAULT_MAX_MSGPOOLS,
-
/* MAX_MSGQS */ BSP_DEFAULT_MAX_MSGQS,
-
/* IO_CHANNEL */ BSP_DEFAULT_IO_CHANNEL,
-
/* IO_OPEN_MODE */ BSP_DEFAULT_IO_OPEN_MODE,
-
0,
-
0
-
}
-
-
_mqx( (MQX_INITIALIZATION_STRUCT_PTR) &MQX_init_struct );
在MQX_init_struct在main函数里被_mqx函数调用,折腾来折腾去最后将kernel_data->INIT.TASK_TEMPLATE_LIST变成MQX_template_list。
-
template_ptr = kernel_data->INIT.TASK_TEMPLATE_LIST;
-
while (template_ptr->TASK_TEMPLATE_INDEX) {
-
if (template_ptr->TASK_ATTRIBUTES & MQX_AUTO_START_TASK) {
-
td_ptr = _task_init_internal(template_ptr, kernel_data->ACTIVE_PTR->TASK_ID,
-
template_ptr->CREATION_PARAMETER, FALSE, NULL, 0);
-
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
-
if (td_ptr == NULL) {
-
_mqx_exit(MQX_OUT_OF_MEMORY);
-
} /* Endif */
-
#endif
-
_task_ready_internal(td_ptr);
-
} /* Endif */
-
++template_ptr;
-
} /* Endwhile */
从这里我们可以看出,凡是被赋予MQX_AUTO_START_TASK属性的task_template_struct,都通过_task_init_internal生成td_struct。当然创建任务也不一定非要使用MQX_template_list静态添加,MQX也允许先创建一个task_template_struct,进而再通过_task_init_internal生成相应的td_struct。比如IDLE任务就是这么创建的。
-
task_template_ptr = (TASK_TEMPLATE_STRUCT_PTR)
-
&kernel_data->IDLE_TASK_TEMPLATE;
-
task_template_ptr->TASK_TEMPLATE_INDEX = IDLE_TASK;
-
task_template_ptr->TASK_STACKSIZE = PSP_IDLE_STACK_SIZE;
-
task_template_ptr->TASK_NAME = MQX_IDLE_TASK_NAME;
-
task_template_ptr->TASK_ADDRESS = _mqx_idle_task;
-
task_template_ptr->TASK_PRIORITY = priority_levels + 1;
-
-
....
-
-
td_ptr = _task_init_internal(
-
(TASK_TEMPLATE_STRUCT_PTR)&kernel_data->IDLE_TASK_TEMPLATE,
-
kernel_data->ACTIVE_PTR->TASK_ID, MQX_IDLE_TASK_PARAMETER, TRUE, NULL, 0);
由上述的介绍可以看出,一个任务的创建的关键在于task_template_struct这个结构体和_task_init_internal函数,至于前者主要提供了任务的相关信息,主要包括任务ID、任务函数、堆栈大小、优先级等。接下来就主要说明_task_init_internal函数了。
-
#if MQX_CHECK_ERRORS
-
if (template_ptr->TASK_PRIORITY > kernel_data->LOWEST_TASK_PRIORITY)
-
{
-
#if MQX_USE_IDLE_TASK
-
if (template_ptr != &kernel_data->IDLE_TASK_TEMPLATE)
-
{
-
#endif /* MQX_USE_IDLE_TASK */
-
-
_task_set_error(MQX_INVALID_TASK_PRIORITY);
-
return (NULL);
-
-
#if MQX_USE_IDLE_TASK
-
}
-
#endif /* MQX_USE_IDLE_TASK */
-
}
-
#endif /* MQX_CHECK_ERRORS */
一开始主要检查优先级,之前说过MQX的优先级是由链表形式组织的,所以不像ucos和rt
thread的数组形式的优先级,理论上可以达到无数个(当然受限于内存的大小)。不过虽然基于链表形式,但考虑到优先级是牵一发动全身的关键结构体,所以MQX只是在初始化的时候配置了优先级链表,之后就不在改动了,具体配置的代码如下:
-
priority_levels = 0;
-
task_template_ptr = kernel_data->INIT.TASK_TEMPLATE_LIST;
-
for (i = 0;task_template_ptr->TASK_TEMPLATE_INDEX && (i < MQX_MAXIMUM_NUMBER_OF_TASK_TEMPLATES); ++i, ++task_template_ptr) {
-
if (priority_levels < task_template_ptr->TASK_PRIORITY) {
-
priority_levels = task_template_ptr->TASK_PRIORITY;
-
} /* Endif */
-
} /* Endfor */
-
kernel_data->LOWEST_TASK_PRIORITY = priority_levels;
-
-
-
....
-
-
priority_levels = kernel_data->LOWEST_TASK_PRIORITY + 2;
-
-
q_ptr = (READY_Q_STRUCT_PTR)_mem_alloc_zero(sizeof(READY_Q_STRUCT) * priority_levels);
-
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
-
if ( q_ptr == NULL ) {
-
return (MQX_OUT_OF_MEMORY);
-
} /* Endif */
-
#endif
-
_mem_set_type(q_ptr, MEM_TYPE_READYQ);
-
-
n = priority_levels;
-
while (n--) {
-
q_ptr->HEAD_READY_Q = (TD_STRUCT_PTR)q_ptr;
-
q_ptr->TAIL_READY_Q = (TD_STRUCT_PTR)q_ptr;
-
q_ptr->PRIORITY = (uint_16)n;
-
-
if (n + kernel_data->INIT.MQX_HARDWARE_INTERRUPT_LEVEL_MAX < ((1 << CORTEX_PRIOR_IMPL) - 1))
-
q_ptr->ENABLE_SR = CORTEX_PRIOR(n + kernel_data->INIT.MQX_HARDWARE_INTERRUPT_LEVEL_MAX);
-
else
-
q_ptr->ENABLE_SR = CORTEX_PRIOR((1 << CORTEX_PRIOR_IMPL) - 2);
-
-
q_ptr->NEXT_Q = kernel_data->READY_Q_LIST;
-
kernel_data->READY_Q_LIST = q_ptr++;
-
}
-
-
-
/*
-
** Set the current ready q (where the ready queue searches start) to
-
** the head of the list of ready queues.
-
*/
-
kernel_data->CURRENT_READY_Q = kernel_data->READY_Q_LIST;
MQX先从MQX_template_list找出最低的优先级,而之后IDLE任务的优先级就是这个最低优先级+1,MQX的LOWEST_TASK_PRIORITY
则是最低优先级+2,由于没有任务比IDLE任务优先级低,所以LOWEST_TASK_PRIORITY
上面是没有任务的。之后MQX会根据LOWEST_TASK_PRIORITY
来创建优先级链表,在本程序中MQX_template_list的最低优先级是9,所以LOWEST_TASK_PRIORITY
就是9+2=11。(我试过设置一个任务优先级为1000,可以运行,但是设为10000的时候就失败了,因为分配优先级链表的时候内存不够用了。)在_task_init_internal中,一旦创建的任务的优先级低于LOWEST_TASK_PRIORITY
,就是会报错返回。所以虽然MQX允许不通过MQX_template_list来创建任务,但考虑到优先级的限制,能在MQX_template_list中添加任务自然最好。
继续返回_task_init_internal的分析,接下来要创建任务的结构体TD_STRUCT和任务的堆栈,在这里有两个分支,一种是事先提供了一块内存区域input_stack_ptr来分配任务结构体和任务的堆栈,就在这块区域里分配,另一种是没有提供这块内存区域,于是MQX就根据task_template_struct里提供堆栈大小信息来动态分配一块,具体工作由_task_alloc_td_internal来完成。
在本程序中,由于没有给任务提供相应的内存区域,所以_task_alloc_td_internal就分配了任务结构体new_td_ptr
和堆栈new_td_ptr->STACK_ALLOC_BLOCK。之后会分配给这个新任务一个TASK_ID,所谓的TASK_ID就和上位机系统的PID一样,是系统识别任务的标志。对于MQX的TASK_ID是由PROCESSOR_NUMBER和TASK_NUMBER组成。PROCESSOR_NUMBER是CPU的识别号内容为BSP_DEFAULT_PROCESSOR_NUMBER,这个变量听起来很复杂其实就是一个1。虽然在K60平台没有体现出来,MQX其实是支持多处理器的,一个任务是由哪一个处理器处理的,都是有任务标识的,但由于K60是单核,所以在本程序中没有意义。而
TASK_NUMBER是MQX分配的任务号,分配起来非常简单:从1开始往后分配,新创建一个任务就把
TASK_NUMBER+1,只要这个TASK_NUMBER没有被其他任务使用就设为该任务的TASK_ID。最后还会将任务插入全局的任务链表,以便以后的进一步管理。
回到_task_init_internal,将任务状态设为阻塞,如果不是根据MQX_template_list中task_template_struct生成任务的,还要将任务对应的task_template_struct放在任务堆栈的前端,保证对于任何一任务,MQX都可以找到对应的task_template_struct。下一步就是关于任务优先级的操作,根据任务的优先级找到对应的优先级链表,注意我们并没有将任务插入链表,因为优先级链表对应的是运行态下的任务,在这里任务是阻塞,因此没有加入链表。
接下来就是构建任务的堆栈了,因为任务切换的本质就是切换堆栈,因此构建一个任务堆栈是为了营造一个切换的上下文环境。完成该功能的函数是_psp_build_stack_frame,之前我在“从异常到任务”这一章中已经说明过了如何从异常切换到任务态,关键就是PSP_STACK_START_STRUCT这个结构体,在本篇中就不在赘述了。
堆栈构建完了之后,就没有特别重要的东西了。之后这个任务经过_task_ready_internal,将它加入到优先级链表中就可以运行了。
阅读(2169) | 评论(0) | 转发(0) |