uC/OS-II学习笔记之任务

本文介绍了uC/OS-II操作系统的任务管理机制,包括任务的状态转换、任务控制块的结构和功能、就绪表的工作原理以及任务调度的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个任务也称做一个线程,是一个简单的程序,该程序可以认为CPU完全只属于自己。实时应用程序的设计过程包括如何把问题分割成多个任务。每个任务都是整个应用的一部分,都被赋予一定的优先级,有自己的一套CPU寄存器和栈空间。

任务状态:休眠态,就绪态,运行态,挂起态及被中断态。

  • 睡眠态:指任务驻留在程序空间(ROM或RAM)还没交给uC/OS-II来管理。把任务交给uC/OS-II是通过函数OSTaskCreate()或函数OSTaskCreateExt()来实现的。这两函数告诉uC/OS-II任务的起始地址在哪?任务创建时用户给任务赋予的优先级是多少?任务要使用多少栈空间?等等。
  • 就绪态:任务一旦建立就进入就绪态准备运行。任务的建立可以是在多任务运行开始之前,也可以动态的由一个运行着的任务 建立。如果多任务已经启动,且一个任务是被另一个任务建立的,而新建立的任务的优先级高于建立它的任务的优先级,则这个刚刚建立的任务将立即得到CPU的使用权。一个任务可以通过调用OSTaskDel()返回到睡眠态,或通过调用该函数让另一个任务进入休眠态。
  • 运行态:调用OSStart()可以启动多任务。OSStart()函数只能在启动时调用一次,该函数运行用户初始化代码中已经建立的、进入就绪态的、优先级最高的任务。优先级最高的任务就这样进入了运行态。任何时刻只能有一个任务处于运行态。就绪态的任务只有当优先级比他高的任务转为等待态或被删除了,才能进入运行态。
  • 等待态:正在运行的任务可通过调用函数OSTimeDly()或函数OSTimeDlyHMSM(),将自身延时一段时间,从而进入等待态。这个2个函数会立即强制执行任务切换,让下一个就绪态中优先级最高的任务运行。等待时间到了后,系统服务函数OSTimeTick()时延时了的任务进入就绪态。正在运行的任务可能需要等待某一事件的发生,可以通过调用一下函数之一实现:OSFlagPend(),OSSemPend(),OSMutexPend(),OSMboxPend()或OSQPend()。如果某个事件未发生,调用上述函数的任务就进入了等待态,直到等待的事件发生了。当任务因等待事件被挂起时,下一个优先级高的任务得到CPU的使用权。当事件发生了或等待超时,被挂起的任务就将入就绪态。事件发生的报告可能来自另一个任务,也可能来自中断服务字程序。 
  • 中断服务态:正在运行的任务是可以被中断的,除非中断被关闭。被中断了的任务于是进入了中断服务态。响应中断时,正在执行的任务被挂起,中断服务子程序控制了CPU的使用权。中断服务子程序可能会报告一个或多个事件的发生,而使一个或多个任务进入就绪态。在这种情况下,从中断服务子程序返回之前,uC/OS-II要判断被中断的任务是否还是就绪任务中优先级最高的。如果中断服务子程序使另一个优先级更高的任务进入了就绪态,则新进入就绪态的这个优先级更高的任务就得以运行;否则原来被中断了的任务将继续运行。

当所有的任务都在等待态时,uC/OS-II将执行空闲任务,即OSTaskIdle()。

任务控制块(OS_TCB)

一旦任务建立,一个任务控制块就被赋值。任务控制块是一个数据结构,当任务的CPU使用权被剥夺时,uC/OS-II用它来保护该任务的状态。当任务重新得到CPU使用权时,任务控制块能够确保任务从当时被中断初丝毫不差的继续执行。OS_TCB全部驻留在RAM中。

typedef struct os_tcb
{
    OS_STK        *OSTCBStkPtr;                                                 /* 指向当前任务堆栈栈顶的指针 */
    #if OS_TASK_CREATE_EXT_EN > 0
    void         *OSTCBExtPtr;                                                        /* 指向用户定义的任务控制块扩展的指针 */
    OS_STK      *OSTCBStkBottom;                                            /* 指向任务堆栈栈低的指针,用户可以用它来确定任务实际需要的栈空间 */
    INT32U        OSTCBStkSize;                                                  /* 栈中可容纳的指针元数目 */
    INT16U        OSTCBOpt;                                                        /* 创建任务时给任务创建扩展函数传递的选择项 */
    INT16U        OSTCBId;                                                           /* 用于存储任务的ID,预留 */
    #endif
    struct os_tcb    *OSTCBNext;                                                 /* 用于任务控制块OS_TCB双向链表的前链接 */
    struct os_tcb    *OSTCBPrev;                                                 /* 用于任务控制块OS_TCB双向链表的后链接 */
    #if ((OS_Q_EN > 0)&&(OS_MAX_OS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
    OE_EVENT    *OSTCBEventPtr;                                            /* 指向事件控制块的指针 */
    #endif
    #if ((OS_Q_EN > 0)&&(OS_MAX_OS > 0)) || (OS_MBOX_EN > 0)
    void        *OSTCBMsg;                                                           /* 指向传递给任务的消息的指针 */
    #endif
    #if (OS_VERSION >= 251)&&(OS_FLAG_EN > 0)&&(OS_MAX_FLAG > 0)
    #if OS_TASK_DEL_EN > 0
    OS_FLAG_NODE    *OSTCBFlagNode;                               /* 指向事件标志节点的指针 */
    #endif
    OS_FLAGS    OSTCBFlagRdy;                                            /* 当任务等待事件标志组时使任务进入就绪的事件标志 */
    #endif
    INT16U        OSTCBDly;                                                      /* 任务延时时间 */
    INT8U        OSTCNStat;                                                       /* 任务的状态字,当等于OS_STAT_READY时任务进入就绪态 */
    INT8U        OSTCBPrio;                                                       /* 任务的优先级,值越小任务的优先级越高 */
    INT8U        OSTCBX;                                                           /* = priority >> 3 */
    INT8U        OSTCBY;                                                           /* = OSMapTbl[priority >> 3] */
    INT8U        OSTCBbitX;                                                       /* = priority & 0x07 */
    INT8U        OSTCBBitY;                                                       /* = OSMapTbl[priority & 0x07] */
    #if OS_TASK_DEL_EN > 0
    BOOLEAN        OSTCBDelReq                                           /* 表示该任务是否需删除 */
    #endif
} OS_TCB;

应用程序中可以有的最多任务数(OS_MAX_TASKS)已经在文件OS_CFG.H中定义。这个最多任务数也决定了分配给用户程序的任务控制块OS_TCB的数目,将OS_MAX_TASKS的数目设置为用户应用程序实际需要的任务数,可以减小RAM的需求量。所有的任务控制块OS_TCB都是放在任务控制块列表数组OSTCBTbl[]中的。

在uC/OS-II初始化时,所有任务控制块OS_TCB都被链接成单向空任务链表。任务一旦建立,空任务控制块指针OSTCBFreeList指向的任务控制块便赋给了该任务,然后OSTCBFreeList的值调整为指向链表中下一个空的任务控制块。一旦任务被删除,任务控制块就还给空任务链表。

任务建立时,函数OS_TCBInit()初始化任务控制块OS_TCB,函数OSTaskCreate()或函数OS_TaskCreateExt()调用任务控制块初始化函数OS_TCBInit()。

就绪表

就绪表是记录进入就绪态的任务的一张表。当任务进入就绪态时uC/OS-II会根据任务优先级在就绪表中设置该任务就绪标志位,当任务退出就绪态时uC/OS-II会根据任务优先级在就绪表中清除该任务就绪标志位,当需要进行任务调度时uC/OS-II会根据就绪表找出优先级最高的就绪任务进入运行态。

每个就绪的任务都放在就绪表中,就绪表中有2个变量:OSRdyGrp和OSRdyTbl[]。就绪表中每8位为一组,即8个任务按优先级分为一组。每组任务就绪状态存放在OSRdyTbl[i]中,OSRdyTbl[i]中的8个位对于8个任务,每一位表示每个不同优先级任务的就绪状态。OSRdyTbl[i]中的任何一位是1时,OSRdyGrp的第i位置1。

  • 使任务进入就绪态

       OSRdyGrp                |= OSMapTbl[prio >> 3];                  /* prio右移3位相当于prio/8,即将任务按每8个任务为一组进行分组。*/

       OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];             /* 将该优先级任务相应组中的相应位置1。 */

       OSMapTbl[]存放的是掩码

OSMapTbl[]的值
数组下标数组掩码值(二进制)
000000001
100000010
200000100
300001000
400010000
500100000
601000000
710000000
  • 使任务脱离就绪态

       if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)     /* 将该优先级的任务相应组中的相应位清0 */

       OSRdyGrp &= ~OSMapTbl[prio >> 3];                                     /* 如果该组中没有就绪任务,则把该组清除 */

  • 找出进入就绪态的优先级最高的任务

       y = OSUnMapTbl[OSRdyGrp];

       x = OSUnMapTbl[OSRdyTbl[y]];

       prio = y << 3 + x;

 为了找到进入就绪态的优先级最高的任务,并不需要从OSRdyTbl中开始扫描任务就绪表,只需查另一张表,即优先级判定表OSUnMapTbl[256]。从优先级判定表中算出优先级值,利用这个优先级值查任务控制块优先级表OSTCBPrioTbl[],得到指向相应任务的任务控制块OS_TCB。

任务调度

uC/OS-II总是运行进入就绪态任务中优先级最高的任务。确定哪个任务优先级最高,下面该哪个任务运行了,这个工作是由调度器完成的。任务级的调度是由函数OSSched()完成的,中断级的调度是由OSIntExt()完成的。uC/OS-II中任务调度的执行时间是常数。

void OS_Sched(void)

{

#if OS_CRITICAL_METHOD == 3

    OS_CPU_SR  cpu_sr;

#endif

    INT8U  y;

    OS_ENTER_CRITICAL();                                                                         /* 进入临界代码段,关中断。 */

    if ((OSInitNesting == 0) && (OSLochNesting == 0))                                 /* 调度是否来自中断服务子程序,或调用了任务调度上锁函数 */

    {                                                                                                                /* 如果没有则进入就绪表找出优先级最高的就绪任务 */

        y = OSUnmapTbl[OSRdyGrp];

        OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

        if (OSPrioHighRdy != OSPrioCur)                                                         /* 检查找出的任务是否为当前运行的任务 */

        {                                                                                                             /* 如果不是则获取优先级最高任务的任务控制块 */

            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

            OSCtxSwCtr++;                                                                                 /* 任务调度次数计数 */

            OS_TASK_SW();                                                                               /* 调用OS_TASK_SW()完成实际上的任务切换 */

         }

    }

    OS_EXIT_CRITICAL();                                                                              /* 推出临界代码段,开中断 */

}

任务级的任务切换OS_TASK_SW()

任务的上下文其实就是CPU中的全部寄存器内容,上下文切换就是任务切换。任务切换代码须恢复该任务在CPU使用权被剥夺时保存下来的全部寄存器的值,以便让这个任务能继续运行。

OS_TASK_SW()是宏调用,通常含有微处理器的软中断指令,因为uC/OS-II假定任务切换是靠中断级代码完成的,uC/OS-II需要的是一条处理器指令,其行为就像是硬件中断(所以称为软中断)。uC/OS-II使用宏定义,将与实际处理器相关的软件中断机制封装起来,使之可以在多种处理器开发平台上移植。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值