ucosiii任务管理相关知识

Posted on 2017-02-17 15:14  自由一份子  阅读(2715)  评论(0编辑  收藏  举报

  只要学习和理解:任务状态

          任务堆栈

          任务控制块

          任务就绪表

          任务调度和切换

  关于任务状态:

  任务堆栈是任务的重要部分,堆栈是在RAM中按照“先进先出(FIFO)”的原则组织的一块连续的内存空间。为了满足任务切换和相应中断时保存CPU寄存器中的内容及任务调用其他函数时的需要,每个任务都应该有自己的堆栈。

  1、任务堆栈的创建:

  UCOSIII中任务堆栈的创建很简单,如原子系列教程中的例子:

  

  注意:任务堆栈的大小不是512字节,真实大小是下面那个数组所占空间的大小,其中,“CPU_STK”为CPU_INT32U类型(“unsigned int”类型),为4字节的,那么任务堆栈START_TASK_STK的大小就是:512*4=2048字节!

  上面是任务堆栈大小相关的部分,那么如何使用那个创建好的堆栈空间呢“

  2、任务堆栈的初始化:

  任务如何才能切换回上一个任务并且还能继续从上次被中断的地方开始运行呢?那就要恢复现场,现场就是CPU的内部各个寄存器。在刚创建一个新任务时,就必须把系统启动这个任务时所需的CPU各个寄存器的初始值事先存放在任务堆栈中。这样当任务获得CPU使用权时,就把任务堆栈的内容复制到CPU的各个寄存器,从而可以让任务顺利的启动并运行。

  2.1、任务堆栈初始化函数:

    OSTaskStkInit();

    此函数在UCOSIII源码”os_cpu_c.c“中大概229-263行;

    函数原型:

    CPU_STK *OSTaskStkInit(OS_TASK_PTR    p_task,

                   void         *p_arg,

                   CPU_STK       *p_stk_base,

                   CPU_STK       *p_stk_Limit,

                   CPU_STK_SIZE   stk_size,

                     OS_OPT       opt)

    注意:*用户一般不会直接操作这个函数,这个函数一般是由”OSTaskCreate()“函数调用;

       *不同CPU对寄存器的操作方式不同,因此在移植UCOSIII时需要用户根据自己所选的CPU来编写任务堆栈初始化函数;(这会回想一下,这对应了之前看原子视频的UCOSII移植部分中对文件做出的各种修改的原因)

  3、之前创建出来的任务堆栈如何使用:

    创建出来的任务堆栈其实是作为任务创建函数”OSTaskCreate()“的参数直接带入即可;

 

  4、任务堆栈栈深:

 

 

    在“OSTaskCreate()”函数中的第7个参数中,这个参数是任务堆栈栈深的设置;

    栈深就是说,任务在使用堆栈时,给他设定一个“堆栈快要用光”的限位,一般限制在堆栈大小的10%,具体先不要纠结这个参数,我想应该是系统需要根据这个数做出什么反应之类。

  5、任务控制块:

    任务控制块是用来记录与任务相关的信息的数据结构,每个任务都要有自己的控制块。任务控制块有用户自行创建,如下

  代码为创建一个任务控制块:

OS_TCB  StartTaskTcb;//创建一个任务控制块

    OS_TCB是一个结构体,描述了任务控制块,任务控制块中的成员变量用户不能直接访问,更不可能改变他们。

    OS_TCB作为一个结构体,其中有些成员采用了条件编译的方式来确定。

    这部分代码在UCOSIII源码“os.h”中大概第939-1045行,这个结构体里面的变量是非常多的;

    这个结构体中的变量的详细讲解在原子的资料“STM32 UCOS开发手册.pdf”中第5章,“任务控制块”中;

  5.1、任务控制块的初始化:

    在前面“OSTaskCreate()”创建任务时,会对任务的任务控制块进行初始化。函数“OS_TaskInitTCB()”用于初始化任务控制块,用户不需要自行初始化任务控制块;

    函数“OS_TaskInitTCB()”源码在UCOSIII源码“os_task.c”中大概第1957-2082行;

  6、UCOSIII任务就绪表:

    6.1优先级

    UCOSIII中任务优先级数由宏“OS_CFG_PRIO_MAX”来配置,UCOSIII中数值越小,优先级越高,最低可用优先级就是“OS_CFG_PRIO_MAX-1”;

    “OS_CFG_PRIO_MAX”这个宏在UCOSIII源码“os_cfg.h”中大概第48行;(源码中,这个宏默认是64,用户可自由修改,理论上可以无限大)

    6.1就绪表

    所谓就绪表,就是有个任务已经准备好了,就等着运行了,如果自己的优先级够高,那么下一个运行的任务就是它了;

    UCOSIII就绪表中有两部分组成:

    1、优先级位映射表:OSPrioTb1[]:用来记录哪个优先级下有任务就绪;

      这个表在“os_prio.c”中有定义,大概41行处:

      

      

    2、就绪任务列表:OSRdyList[]:用来记录每一个优先级下所有就绪的任务;

    3、下表好好理解理解:

 

    6.3 系统如何找到已经就绪了的最高优先级的任务?

      函数 "OS_PrioGetHighest()"用于找到就绪了的最高优先级的任务;

      源码在“os_prio.c”大概第85行处;

      这个函数的作用就是找出最高优先级的任务,找最高优先级时是硬件执行的,速度很快,基本原理如下:

 

    函数会一直循环的找“1”,OSPrioTbl[0]里面如果每个位都为0,那就继续往OSPrioTbl[1]中找,直到碰到“1”,就算是找出来了当前已经就绪了的最高优先级的任务,但是此时还不能找到具体哪个任务,因为一个优先级下面有多个任务;

    6.4就绪任务列表

      刚才上边说了,找到最高优先级后,还不能确定具体是哪个任务,这时就用到就绪任务列表“OSRdyList[]”(上边提到过)了;

      在这个列表中,系统会找出最先需要执行的任务,具体要执行哪一个,是按照这个里面的一个链表执行的,最先执行的是链表头那个任务,执行完后就插到尾去了,具体细节先不要过分研究;

  7、任务调度和切换

    7.1

    任务调度就是中止当前正在运行的任务转而去执行其他任务。

    UCOSIII是可剥夺型内核,因此当一个高优先级的任务准备就绪,并且此时发生了任务调度,那么这个高优先级的任务就会获得CPU的使用权!

    UCOSIII中的任务调度是由任务调度器来完成的,任务调度器有2中:任务级调度器中断级调度器

    任务级调度器,是个函数:“OSSched()”;,此函数是在普通任务中使用;

    中断级调度器,是个函数:“OSIntExit()”,当在退出外部中断服务函数的时候会使用中断级任务调度,这个函数一般写在中断服务函数里面;

    任务调度器使用方法,在原子视频第8讲,UCOSIII任务管理(下)中的第6分左右,有使用方法的说明,就是上边两个函数只要调用一次,系统就会发生一次任务切换;

    7.2 任务调度点:

    任务调度点就是发生任务调度的时间点或者说节点,就是在哪个地方发生的任务调度;(用户可以自己调用一下调度函数来发生任务调度)

    那么,都有哪些任务调度点呢:

 

    7.3 调度器的上锁和解锁

    有时候我们不希望发生任务调度,因为有些时候是不能被打断的,比如正在初始化某个外设的时候,这时就需要对调度器上锁,上锁函数:“OSSchedLock()”;当想要恢复调度器的时候,就需要解锁,解锁函数:“OSSchedUnlock()”;

    7.4 时间片轮转调度


  因为有多任务,每个任务执行指定的时间片,然后轮到下一个任务执行,这个过程就是时间片轮转调度。时间片轮转调度器:“OS_SchedRoundRobin()”;

  注意:任务可以放弃自己的时间片,比如一个任务本来要执行4个时间片,执行3个时间片后,出于某种原因,不想继续执行,此时它是可以放弃剩余1个时间片的;

    7.5 任务切换

 

上边两个函数在汇编文件“os_cpu_a.asm”中

 

 

注:任务切换是包含在任务调度中的;

  8、UCOSIII系统初始化:

    在使用UCOSIII之前我们必须先初始化UCOSIII,函数“OSIniy()”用来完成UCOSIII的初始化,而且OSInit()必须先于其它UCOSIII函数调用,包括OSStart()。

  9、UCOSIII系统启动:

    使用函数"OSStart()"来启动UCOSIII;