Chinaunix首页 | 论坛 | 博客
  • 博客访问: 178115
  • 博文数量: 20
  • 博客积分: 125
  • 博客等级: 入伍新兵
  • 技术积分: 985
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-08 13:48
个人简介

热爱开源,喜欢分析操作系统架构

文章分类

全部博文(20)

文章存档

2013年(17)

2012年(3)

分类: 嵌入式

2013-07-14 21:18:58


    前面说了一下任务的数据结构,就算是为了本文做个铺垫,现在我来说一下MQX是如何创建一个任务的。

    MQX创建任务有多种方式,但创建任务的前提是必须有task_template_struct。这个task_template_struct可以通过在MQX_template_list里静态添加,也可以在其他地方添加(不建议这么做,原因后面会提)

举个静态添加的例子,在hello.c中:


点击(此处)折叠或打开

  1. const TASK_TEMPLATE_STRUCT MQX_template_list[] =
  2. {
  3.    /* Task Index, Function, Stack, Priority, Name, Attributes, Param, Time Slice */
  4.     { WORLD_TASK, world_task, 1000, 9, "world", MQX_AUTO_START_TASK, 0, 0 },
  5.     { HELLO_TASK, hello_task, 1000, 8, "hello", 0, 0, 0 },
  6.     { 0 }
  7. }

    这个MQX_template_list在这里出现了:

点击(此处)折叠或打开

  1. const MQX_INITIALIZATION_STRUCT MQX_init_struct =
  2. {
  3.     /* PROCESSOR_NUMBER */ BSP_DEFAULT_PROCESSOR_NUMBER,
  4.     /* START_OF_KERNEL_MEMORY */ BSP_DEFAULT_START_OF_KERNEL_MEMORY,
  5.     /* END_OF_KERNEL_MEMORY */ BSP_DEFAULT_END_OF_KERNEL_MEMORY,
  6.     /* INTERRUPT_STACK_SIZE */ BSP_DEFAULT_INTERRUPT_STACK_SIZE,
  7.     /* TASK_TEMPLATE_LIST */ MQX_template_list,
  8.     /* MQX_HARDWARE_INTERRUPT_LEVEL_MAX*/ BSP_DEFAULT_MQX_HARDWARE_INTERRUPT_LEVEL_MAX,
  9.     /* MAX_MSGPOOLS */ BSP_DEFAULT_MAX_MSGPOOLS,
  10.     /* MAX_MSGQS */ BSP_DEFAULT_MAX_MSGQS,
  11.     /* IO_CHANNEL */ BSP_DEFAULT_IO_CHANNEL,
  12.     /* IO_OPEN_MODE */ BSP_DEFAULT_IO_OPEN_MODE,
  13.     0,
  14.     0
  15. }

  16.  _mqx( (MQX_INITIALIZATION_STRUCT_PTR) &MQX_init_struct );

    在MQX_init_struct在main函数里被_mqx函数调用,折腾来折腾去最后将kernel_data->INIT.TASK_TEMPLATE_LIST变成MQX_template_list。

点击(此处)折叠或打开

  1. template_ptr = kernel_data->INIT.TASK_TEMPLATE_LIST;
  2.     while (template_ptr->TASK_TEMPLATE_INDEX) {
  3.         if (template_ptr->TASK_ATTRIBUTES & MQX_AUTO_START_TASK) {
  4.             td_ptr = _task_init_internal(template_ptr, kernel_data->ACTIVE_PTR->TASK_ID,
  5.                             template_ptr->CREATION_PARAMETER, FALSE, NULL, 0);
  6. #if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
  7.             if (td_ptr == NULL) {
  8.                 _mqx_exit(MQX_OUT_OF_MEMORY);
  9.             } /* Endif */
  10. #endif
  11.             _task_ready_internal(td_ptr);
  12.         } /* Endif */
  13.         ++template_ptr;
  14.     } /* 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任务就是这么创建的。


点击(此处)折叠或打开

  1. task_template_ptr = (TASK_TEMPLATE_STRUCT_PTR)
  2.     &kernel_data->IDLE_TASK_TEMPLATE;
  3.     task_template_ptr->TASK_TEMPLATE_INDEX = IDLE_TASK;
  4.     task_template_ptr->TASK_STACKSIZE = PSP_IDLE_STACK_SIZE;
  5.     task_template_ptr->TASK_NAME = MQX_IDLE_TASK_NAME;
  6.     task_template_ptr->TASK_ADDRESS = _mqx_idle_task;
  7.     task_template_ptr->TASK_PRIORITY = priority_levels + 1;
  8.     
  9.     ....
  10.     
  11.     td_ptr = _task_init_internal(
  12.                  (TASK_TEMPLATE_STRUCT_PTR)&kernel_data->IDLE_TASK_TEMPLATE,
  13.                  kernel_data->ACTIVE_PTR->TASK_ID, MQX_IDLE_TASK_PARAMETER, TRUE, NULL, 0);

由上述的介绍可以看出,一个任务的创建的关键在于task_template_struct这个结构体和_task_init_internal函数,至于前者主要提供了任务的相关信息,主要包括任务ID、任务函数、堆栈大小、优先级等。接下来就主要说明_task_init_internal函数了。

点击(此处)折叠或打开

  1. #if MQX_CHECK_ERRORS
  2.     if (template_ptr->TASK_PRIORITY > kernel_data->LOWEST_TASK_PRIORITY)
  3.     {
  4. #if MQX_USE_IDLE_TASK
  5.         if (template_ptr != &kernel_data->IDLE_TASK_TEMPLATE)
  6.         {
  7. #endif /* MQX_USE_IDLE_TASK */

  8.             _task_set_error(MQX_INVALID_TASK_PRIORITY);
  9.             return (NULL);

  10. #if MQX_USE_IDLE_TASK
  11.         }
  12. #endif /* MQX_USE_IDLE_TASK */
  13.     }
  14. #endif /* MQX_CHECK_ERRORS */
一开始主要检查优先级,之前说过MQX的优先级是由链表形式组织的,所以不像ucos和rt thread的数组形式的优先级,理论上可以达到无数个(当然受限于内存的大小)。不过虽然基于链表形式,但考虑到优先级是牵一发动全身的关键结构体,所以MQX只是在初始化的时候配置了优先级链表,之后就不在改动了,具体配置的代码如下:

点击(此处)折叠或打开

  1. priority_levels = 0;
  2.     task_template_ptr = kernel_data->INIT.TASK_TEMPLATE_LIST;
  3.     for (i = 0;task_template_ptr->TASK_TEMPLATE_INDEX && (i < MQX_MAXIMUM_NUMBER_OF_TASK_TEMPLATES); ++i, ++task_template_ptr) {
  4.         if (priority_levels < task_template_ptr->TASK_PRIORITY) {
  5.             priority_levels = task_template_ptr->TASK_PRIORITY;
  6.         } /* Endif */
  7.     } /* Endfor */
  8.     kernel_data->LOWEST_TASK_PRIORITY = priority_levels;
  9.     
  10.     
  11. ....

  12.   priority_levels = kernel_data->LOWEST_TASK_PRIORITY + 2;

  13.    q_ptr = (READY_Q_STRUCT_PTR)_mem_alloc_zero(sizeof(READY_Q_STRUCT) * priority_levels);
  14. #if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
  15.    if ( q_ptr == NULL ) {
  16.       return (MQX_OUT_OF_MEMORY);
  17.    } /* Endif */
  18. #endif
  19.    _mem_set_type(q_ptr, MEM_TYPE_READYQ);

  20.    n = priority_levels;
  21.    while (n--) {
  22.       q_ptr->HEAD_READY_Q = (TD_STRUCT_PTR)q_ptr;
  23.       q_ptr->TAIL_READY_Q = (TD_STRUCT_PTR)q_ptr;
  24.       q_ptr->PRIORITY = (uint_16)n;

  25.       if (n + kernel_data->INIT.MQX_HARDWARE_INTERRUPT_LEVEL_MAX < ((1 << CORTEX_PRIOR_IMPL) - 1))
  26.         q_ptr->ENABLE_SR = CORTEX_PRIOR(n + kernel_data->INIT.MQX_HARDWARE_INTERRUPT_LEVEL_MAX);
  27.       else
  28.         q_ptr->ENABLE_SR = CORTEX_PRIOR((1 << CORTEX_PRIOR_IMPL) - 2);

  29.       q_ptr->NEXT_Q = kernel_data->READY_Q_LIST;
  30.       kernel_data->READY_Q_LIST = q_ptr++;
  31.    }


  32.    /*
  33.    ** Set the current ready q (where the ready queue searches start) to
  34.    ** the head of the list of ready queues.
  35.    */
  36.    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,将它加入到优先级链表中就可以运行了。  


阅读(4471) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~