ustcanycall / OS

0 stars 0 forks source link

uC/OS-II总结 #20

Open ustcanycall opened 10 years ago

ustcanycall commented 10 years ago

1.实时操作系统内核概述

1.1实时操作系统的设计要求

实时操作系统所遵循的设计原则是采用各种算法和策略,始终保证系统行为的可预测性。即在任何情况下,在系统运行的任何时刻,操作系统的任务配置策略都能为争夺资源的多个实时任务合理分配资源,使每个实时任务的实时性要求都能够得到满足。 因此,实时操作系统主要关注的不是系统的平均表现,而是要求在最坏情况下也能满足每一个任务的实时性要求

1.2可抢占与不可抢占内核

根据内核是否可以被剥夺(抢占),实时操作系统内核可分为不可剥夺型内核和可剥夺型内核,两者的主要区别在于中断返回时是否执行新的调度:可剥夺型内核在中断返回时执行新的调度,而不可剥夺型内核则在任务执行完成后执行新的调度

1.3可重入函数与不可重入函数

可重入函数是指函数代码在运行过程中被中断,在中断返回同时仍然能够恢复到原来的状态继续准确执行的函数,而不可重入函数在在运行过程中不可以被中断。简单地说,如果一个外部中断改变了当前函数中的关键运行环境(变量的值,代码执行位置),则该函数是不可重入的,否则为可重入的。 一般来说,如果某个函数在执行过程中涉及直接对全局数据或公共数据进行存取,则该函数是不可重入的。 例如,以下代码是不可重入的

//假设Exam是int型全局变量,函数Sqaure_Exam返回Exam平方值。
unsigned int example( int para )
{
unsigned int temp;
Exam = para; // (**)
temp = Square_Exam( );
return temp;
}

此函数若被多个进程调用的话,其结果可能是未知的,因为当(_)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam赋与另一个不同的para值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。 此函数应如下改进_。

//假设Exam是int型全局变量,函数Sqaure_Exam返回Exam平方值。
unsigned int example( int para )
{
unsigned int temp;
[申请信号量操作] //(1)
Exam = para;
temp = Square_Exam( );
[释放信号量操作]
return temp;
}

(1)若申请不到“信号量”,说明另外的进程正处于给Exam赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能再使用本信号。 保证函数的可重入性的方法:在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量),对于要使用的全局变量要加以保护(如采取关中断、信号量等方法),这样构成的函数就一定是一个可重入的函数。

2.uC/OS-II 任务管理

任务是uc/OS-II操作系统管理事件的基本单元,一个任务必然包括任务代码、任务栈空间及任务控制块一个任务的基本信息被存储在任务控制块中。 任务的基本管理包括任务的创建、删除、挂起、恢复以及设置任务名称等。这些操作不断地改变任务的基本属性,引发新的任务级调度,是当前系统中所有任务在各个任务状态间切换,从而完成多任务协同工作。

2.1任务控制块

μC/OS-Ⅱ是通过任务控制块来管理任务的。任务控制块是一个基于链表的数据结构,任务控制块主要用于记录任务的堆栈栈顶指针、指向下一个任务控制块的指针、任务等待的延迟时间、任务的当前状态标志与任务的优先级别等一些与任务管理有关的属性。

2.1.1任务控制块的基本结构

任务控制块(TCB)用来存储一个任务的当前属性。任务的主要属性如下:

多个任务靠任务控制块组成一个任务链表2

2.1.2系统初始化时任务控制块链表

当进行系统初始化时,初始化函数会按用户提供的任务数为系统创建具体相应数量的任务控制块并把它们链接为一个链表。由于这些任务控制块还没有对应的任务,故这个链表叫做空任务块链表,即相当于是一些空白的身份证。全局变量_OSTCBFreeList_指向第一个未被使用的任务控制块。 空白任务控制块链表 3

为提高任务控制块的访问效率,即可以根据任务优先级快速访问到该优先级的任务控制块,系统定义了一个指针数组_OSTCBPrioTbl[]_。 任务控制块初始化示意图 4

2.1.3任务运行时任务控制块链表

系统定义了全局变量_OSTCBList_指针执行第一个已经被使用的任务控制块,实际运行过程中,系统还定义了指向当前正在运行的任务控制块的指针变量_OCTCBCur_,从而便于对当前运行的任务控制块进行修改。另外,还定义了指向最高优先级的任务控制块指针_OSTCBHighRdy_,从而便于任务切换。 最终,进行任务添加和删除操作后任务控制块全局变量的可能情况如下所示。 5

2.2系统任务

uc/OS-II操作系统提供两个系统级任务:空闲任务和统计任务。使用空闲任务的目的是让CPU在没有其他任务时有事可做,空闲任务的优先级最低。统计任务则是统计CPU的利用率,统计任务优先级仅比空闲任务高

2.2.1空闲任务

在多任务系统运行时,系统经常会在某个时间内无用户任务可运行而处于所谓的空闲状态,为了使CPU 在没有用户任务可执行的时候有事可做,uC/OS-II 提供了一个叫做空闲任务。

    void OSTaskldle(void* pdata)
    {
    #if OS_CRITICAL_METHOD==0;
    OS_CPU_SR cpu_sr;
    #endif
    pdata=pdata;//防止某些编译器报错
    for(;;)
    {
    OS_ENTER_CRITICAL();//关闭中断
    OSdleCtr++;
    OS_EXIT_CRITICAL();//开放中断
    }
    }

uC/OS-II 规定,一个用户应用程序必须使用这个空闲任务,而且这个任务是不能用软件来删除的。

2.2.2统计任务

uC/OS-II 提供的另一个系统任务是统计任务OSTaskStat()。这个统计任务每秒计算一次CPU在单位时间内的时间,并把计算结果以百分比的形式存放在变量OSCPUsage 中,以便应用程序通过访问它来了解CPU 的利用率,所以这个系统任务OSTaskStat()叫做统计任务。

2.3任务状态

对于单处理器来说,任何时刻仅有一个任务处于运行状态,所有其他任务都处于其他转台。uC/OS-II操作系统定义了任务基本状态如下。

#define  OS_STAT_RDY             0x00u      //准备就绪
#define  OS_STAT_SEM             0x01u      //信号量阻塞
#define  OS_STAT_MBOX           0x02u       //消息邮箱阻塞
#define  OS_STAT_Q                  0x04u       //消息队列阻塞
#define  OS_STAT_SUSPEND        0x08u       //挂起,暂停运行
#define  OS_STAT_MUTEX          0x10u       //互斥事件阻塞
#define  OS_STAT_FLAG            0x20u      //事件标识阻塞
#define  OS_STAT_PEND_ANY   (OS_STAT_SEM | OS_STAT_MBOX | OS_STAT_Q | OS_STAT_MUTEX | OS_STAT_FLAG) //任意一个事件阻塞

在uC/OS操作系统中,任务的主要状态包括就绪、运行、阻塞/暂停、休眠(停止)和被中断。任何一个任务一旦被创建,就开始在这些状态间切换;当不需要该任务时,将其从任务链表中删除。 6

2.4任务管理函数

在os_tack.c中定义了所有任务管理函数,它们分别是

OSTaskChangePrio      //改变任务优先级
OSTaskCreate              //创建任务
OSTaskCreateext         //创建扩展任务
OSTaskDel                   //删除任务
OSTaskDelReq            //请求删除任务
OSTaskNameGet         //获取任务名称
OSTaskNameSet         //设置任务名称
OSTaskSuspend         //挂起任务
OSTaskResume          //恢复挂起任务
OSTaskStkChk           //任务堆栈检查
OS_TaskStkClr           //清理任务堆栈
OSTaskQuery             //查询任务状态

3.uc/OS-II任务级调度机制

uc/OS-II操作系统采用基于优先级的调度算法,而且只采用了这种调度算法。 uc/OS-II的优先级调度算法原理如下:给每一个任务分配一个唯一的优先级,各优先级用一个整型数值标示。某优先级的值越大,其优先级越低。也就是说,如果当前操作系统准备进行调度,有两个任务处于就绪状态时,系统将优先执行优先级别高的任务。 uc/OS-II操作系统有两种调度方式:优先级任务调度和中断级任务调度。uc/OS-II操作系统完成中断后允许进行新的调度,因此 uc/OS-II是抢占式强实时性操作系统,这是 uc/OS-II操作系统内核的重要特性

ustcanycall commented 10 years ago

3.1uc/OS-II任务就绪表

为了标示当前系统中哪些任务处于就绪状态,以便于系统唉调度时可以快速访问到处于就绪状态的最高优先级任务,uc/OS-II操作系统定义了如下两个全局变量构成任务就绪表。

 OS_EXT INT8U OSRdyGrp;    //标识某范围内有任务处于就绪状态
 OS_EXT INT8U OSRdyTbl[OS_RDY_TBL_SIZE]  //就绪表,表示某优先级任务处于就绪状态

任务就绪表示意图如下图所示 7

举例优先级为29的任务绪状态位置 8

3.2获取最高优先级任务

uc/OS-II通过OSUnMapTbl来获快速最高优先级的任务。 OSUnMapTbl的构造方法如下。 表中第n+1个成员OSUnMapTbl[n]的值是从Bit0开始,搜索值为n的二进制代码中第1次出现1的位数。例如:

OSUnMapTbl[1]  =0    //(1)d=(0000 0001)b   第0位有1,故值为0
OSUnMapTbl[20]=2    //(20)d=(0001 0100)b 第2位为1,故值为2

OSUnMapTbl的内容预定如下:

INT8U  const  OSUnMapTbl[256] = {
    0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x00 to 0x0F                             */
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x10 to 0x1F                             */
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x20 to 0x2F                             */
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x30 to 0x3F                             */
    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x40 to 0x4F                             */
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x50 to 0x5F                             */
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x60 to 0x6F                             */
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x70 to 0x7F                             */
    7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x80 to 0x8F                             */
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x90 to 0x9F                             */
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xA0 to 0xAF                             */
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xB0 to 0xBF                             */
    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xC0 to 0xCF                             */
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xD0 to 0xDF                             */
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xE0 to 0xEF                             */
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0        /* 0xF0 to 0xFF                             */
};

获取最高优先级就绪任务的步骤

y=OSUnMapTb;[OSRdyGrp]=OSUnMapTbl[7]=0;
OSUnMapTbl[OSRdyTbl[0]]=OSUnMapTbl[2]=1;
OSPrioHighRdy=(INT8U)((0<<x)+1)=1;

从而最高优先级的值为1.

3.3uc/OS-II 任务级任务调度

任务级的调度器由函数OSSched()来实现.

void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3                            /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr = 0;
#endif
    OS_ENTER_CRITICAL();
    if (OSIntNesting == 0) {                           /* Schedule only if all ISRs done and ...       */
        if (OSLockNesting == 0) {                      /* ... scheduler is not locked                  */
            OS_SchedNew();
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endif
                OSCtxSwCtr++;                          /* Increment context switch counter             */
                OS_TASK_SW();                          /* Perform a context switch                     */
            }
        }
    }
    OS_EXIT_CRITICAL();
}

tips:

调用OS_Sched()的情况总结

3.4任务级任务切换

任务切换的工作是由宏OSCtxSw()来执行。 任务级任务切换基本过程:

  1. 把全部CPU寄存器(包括堆栈指针寄存器和PC寄存器)推入当前任务堆栈;
  2. 调用OSTaskSwHook()函数;
  3. OSPrioCur = OSPrioHighRdy;
  4. OSTCBCur = OSTCBHighRdy;
  5. 设置处理器的堆栈指针寄存器为 OSTCBHighRdy->OSTCBStkPtr;
  6. 恢复所有处理器的寄存器内容(不包括堆栈指针寄存器和PC寄存器);
  7. 恢复PC寄存器并开始执行PC指向的指令。
ustcanycall commented 10 years ago

9 tips

4.uc/OS-II系统启动过程

在uc/OS-II操作系统启动过程中执行了两个非常重要的函数:OSInit()OSStart()。只有当执行完OSStart()后,系统的控制权才真正被交给多任务操作系统。

4.1OSInit()函数初始化分析

OSInit()函数主要完成初始化操作,主要用于初始化系统的全局信息。此函数必须在其他任务运行前执行。

void  OSInit (void)
{
#if OS_VERSION >= 204
    OSInitHookBegin();                                           /* Call port specific initialization code   */
#endif

    OS_InitMisc();                                               /* 初始化部分全局变量      */

    OS_InitRdyList();                                            /* 初始化任务就续表                */

    OS_InitTCBList();                                            /* 初始化空闲TCB链表      */

    OS_InitEventList();                                          /*初始化事件控制块链表    */

#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
    OS_FlagInit();                                               /* 初始化事件组标志结构     */
#endif

#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
    OS_MemInit();                                                /* 初始化内存管理           */
#endif

#if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
    OS_QInit();                                                  /* 初始化消息队列 */
#endif

    OS_InitTaskIdle();                                           /* 创建空闲任务                     */
#if OS_TASK_STAT_EN > 0
    OS_InitTaskStat();                                           /* 创建统计任务                */
#endif

#if OS_VERSION >= 204
    OSInitHookEnd();                                             /* Call port specific init. code            */
#endif

#if OS_VERSION >= 270 && OS_DEBUG_EN > 0
    OSDebugInit();
#endif
}

4.2OSStart()函数启动系统分析

当一切准备就绪且所有需要首先创建的任务已经被创建后,系统将调用OSStart()函数启动uc/OS-II操作系统。此函数基本过程如下。 (1) 从就绪标中查找到最高优先级任务的优先级值和该任务的TCB,并设置全局变量OSPrioCur和OSTCBCur。 (2) 将内核设置为运行状态,并调用OSStartHighRdy()函数运行最高优先级任务

void  OSStart (void)
{
    if (OSRunning == FALSE) {
        OS_SchedNew();                               /* Find highest priority's task priority number   */
        OSPrioCur     = OSPrioHighRdy;
        OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run    */
        OSTCBCur      = OSTCBHighRdy;
        OSStartHighRdy();                            /* Execute target specific code to start task     */
    }
}

5.时钟任务和时钟管理

系统是中人物是系统中运行的最为重要的任务之一,它负责时钟中断的管理。系统时钟计数器每隔一个时间段将产生一个时钟中断,从而更新依赖于系统适中的所有任务和管理变量。

5.1创建系统时钟任务

在系统启动前,创建系统时钟任务,使其处于最高优先级

OSTaskCreate(timer_Task,...,0);

5.2时钟中断服务程序 OSTimeTick()

uc/OS-II将OSTimeTick()函数定义为时钟中断服务程序,此函数的主要功能如下。 (1) 更新系统时钟计数器全局变量OSTime (2) 更新当前系统中等待时间延迟的所有任务,并将等待超时的任务标志为就绪状态

void  OSTimeTick (void)
{
    ......
#if OS_TIME_GET_SET_EN > 0
    OS_ENTER_CRITICAL();                                   /* Update the 32-bit tick counter               */
    OSTime++;
    OS_EXIT_CRITICAL();
#endif
    .........
        ptcb = OSTCBList;                                  /* Point at first TCB in TCB list               */
        while (ptcb->OSTCBPrio != OS_IDLE_PRIO) {          /* Go through all TCBs in TCB list              */
            OS_ENTER_CRITICAL();
            if (ptcb->OSTCBDly != 0) {                     /* No, Delayed or waiting for event with TO     */
                if (--ptcb->OSTCBDly == 0) {               /* Decrement nbr of ticks to end of delay       */
                                                           /* Check for timeout                            */
                    if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
                        ptcb->OSTCBStat   &= ~OS_STAT_PEND_ANY;                /* Yes, Clear status flag   */
                        ptcb->OSTCBPendTO  = TRUE;                             /* Indicate PEND timeout    */
                    } else {
                        ptcb->OSTCBPendTO  = FALSE;
                    }

                    if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {  /* Is task suspended?       */
                        OSRdyGrp               |= ptcb->OSTCBBitY;             /* No,  Make ready          */
                        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                    }
                }
            }
            ptcb = ptcb->OSTCBNext;                        /* Point at next TCB in TCB list                */
            OS_EXIT_CRITICAL();
        }
    }
}

OSTimeTick在每次时钟节拍中断都会被调用一次,同时会进行中断级的任务切换。中断级任务切换是抢占式内核的重要标志。

void OSTickISR()
{
    OSIntEnter();
    OSTimeTick();
    OSIntExit();
}

5.3系统时间管理函数

时间管理函数主要包括:

6.uc/OS-II任务间通信和同步机制

多任务系统中任务间的通信非常频繁,因此需要由操作系统提供一种任务间的通信和同步机制。事件和事件组标志是uc/OS-II管理任务间同步和通信的机制

6.1事件管理机制

6.1.2 事件控制块

事件控制块的定义如下

#if (OS_EVENT_EN > 0) && (OS_MAX_EVENTS > 0)                                                             (1)
typedef struct {
    INT8U   OSEventType;                   (2)
    INT8U   OSEventGrp;                    (3)
    INT16U  OSEventCnt;                    (4)
    void   *OSEventPtr;                    (5)
    INT8U   OSEventTbl[OS_EVENT_TBL_SIZE]; (6)
} OS_EVENT;
#endif

注释: (1)条件编译,决定是否编译此数据结构的定义,减少代码量; (2)定义事件具体类型,2.52版本的uC/OS II共包含五种类型,声明如下:

define OS_EVENT_TYPE_UNUSED 0

define OS_EVENT_TYPE_MBOX 1

define OS_EVENT_TYPE_Q 2

define OS_EVENT_TYPE_SEM 3

define OS_EVENT_TYPE_MUTEX 4

define OS_EVENT_TYPE_FLAG 5

(3)(6)OSEventGrp与OSEventTbl[]的作用类似于OSRdyGrp与OSRdyTbl[],存放等待某事件的任务(就绪状态的任务); (4)当OSEventType为OS_EVENT_TYPE_SEM,OSEventCnt是用于信号量的计数器;当当OSEventType为OS_EVENT_TYPE_MUTEX,OSEventCnt用于互斥信号量与优先级继承优先级(PIP)的计数器; (5)指向消息或队列结构的指针,也就是说只有OSEventType为OS_EVENT_TYPE_MBOX或OS_EVENT_TYPE_Q时才有用。 事件控制块结构 11 从上图 中可以看出ECB中也包含一个类似就绪表的结构,将一个任务放入事件的等待任务列表的操作与就绪表的相关操作完全相同。

6.1.2 事件控制块管理

在uC/OS II中,事件控制块的总数由用户所需要的信号量、邮箱和消息队列的总数决定。该值由OS_CFG.H 中的#define OS_MAX_EVENTS定义。在调用OSInit()时,执行了OS_InitEventList()函数,所有事件控制块被链接成一个单向链表——空闲事件控制块链表。每当建立一个信号量、邮箱或者消息队列时,就从该链表中取出一个空闲事件控制块,并对它进行初始化。调用删除信号量、互斥量、邮箱以及队列的函数,可将事件控制块放回到空余事件控制块列表中。 10

6.2 单一消息传递时间:消息邮箱

Mbox用于多任务间单一消息的传递,uC/OS-II使用ECB管理Mbox的基本信息,OSEventPtr指向创建Mbox时指定的内存空间。事件的创建由具体的事件管理程序实现。主要包含在C源文件OS_MBOX.C中。

OS_EVENT *OSMboxCreate(void *msg);          //创建一个消息邮箱
void *OSMboxPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);    //阻塞式读取消息
void *OSMboxAccept(OS_EVENT *pevent);     //非阻塞式读取消息
INT8U OSMboxPost(OS_EVENT *pevent, void *msg);    //发送消息到消息邮箱
INT8U OSMboxPostOpt(OS_EVENT *pevent, void *msg, INT8U opt);   //按照指定方式发送数据到消息邮箱
OS_EVENT *OSMboxDel(OS_EVENT *pevent, INT8U opt, INT8U *err);   //删除消息邮箱
INT8U OSMboxQuery(OS_EVENT *pevent, OS_MBOX_DATA *);       //获取消息邮箱基本信息

13 12

6.3 多消息传递时间:消息队列

msgQ是uC/OS-II任务间通信的机制,可实现多条消息传递,即可以同时存储多条消息。uC/OS-II使用循环队列管理机制。主要包含在C源文件OS_Q.C中。

6.3.1 消息队列的数据结构

OS_EXT OS_Q *OSQTbl[OS_MAX_QS];   //QCB结构体数组
OS_EXT OS_Q *OSQFreeList;                    //空闲QCB头指针
typedef struct os_q{                                //消息队列控制块
      struct os_q *OSQPtr;        //用于构建空闲QCB链表

      void **OSQStart;              //指向msgQ指针数组的起始位置

      void **OSQEnd;               //指向msgQ指针数组的结束位置

      void **OSQIn;    //指向msgQ指针数组下一个可以插入消息的位置

      void **OSQOut; //指向msgQ指针数组下一个可以读出消息的位置

      INT16U OSQSize;            //msgQ指针数组的大小

      INT16U OSQEntries;              //msgQ指针数组当前可以读取的消息个数
}OS_Q;

14

msgQ管理:使用指针数组存储所有消息的位置;使用QCB标识指针数组中消息的基本信息;使用ECB管理整个msgQ。QCB在编译时分配空间,即当前系统中可用的msgQ个数是预先设置的,系统运行时不能修改。

6.3.2 消息队列管理函数

OS_EVENT *OSQCreate(void **start, INT16U size);
INT8U OSQPost(OS_EVENT *pevent, void *msg); //发送消息到队尾
INT8U OSQPostFront(OS_EVENT *pevent, void *msg);      //msg至队首
INT8U OSQPostOpt(OS_EVENT *pevent, void *msg, INT8U opt);
void *OSQPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
void *OSQAccept(OS_EVENT *pevent, INT8U *err);
OS_EVENT *OSQDel(OS_EVENT *pevent, INT8U opt, INT8U *err);
INT8U OSQQuery(OS_EVENT *pevent, OS_Q_DATA*);
INT8U OSQFlush(OS_EVENT *pevent);

15

6.4任务同步机制:信号量

Sem主要用来实现任务间同步及标识某类资源的可用个数,即某个特定资源可供多少任务同时使用。主要包含在C源文件OS_SEM.C中。

OS_EVENT *OSSemCreate(INT16U cnt);
void OSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
INT16U OSSemAccept(OS_EVENT *pevent);
INT8U OSSemPost(OS_EVENT *pevent);
OS_EVENT *OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *err);
INT8U OSSemQuery(OS_EVENT *pevent, OS_SEM_DATA*);
void OSSemSet(OS_EVENT *pevent, INT16U cnt, INT8U *err);

16

6.5 呼哧时间管理机制:互斥锁

Mutex用来实现对资源的排他性访问,可能引起优先级反转。任何任务在占有某个互斥锁事件时,都不能阻塞等待其它任何事件,否则会造成死锁。主要包含在C源文件OS_MUTEX.C中。 uC/OS-II使用ECB管理Mutex,其成员变量OSEventCnt:高8位存储Mutex被使用时提供给任务的prio;低8位在没有任务占有Mutex时为0xFF,否则为占有任务的prio

6.5.1 优先级翻转

优先级反转是指,低优先级任务占有高优先级任务运行所需的资源,而使高优先级不得不等低优先级任务把资源释放才能执行。 优先级反转及优先级反转避免分别如下图所示: 17 18

6.5.2提升/恢复优先级

a) 提升Mutex拥有者任务的优先级的相关操作:

#define  OS_MUTEX_KEEP_LOWER_8   0x00FF
#define  OS_MUTEX_KEEP_UPPER_8   0xFF00
#define  OS_MUTEX_AVAILABLE      0x00FF

OS_EVENT *OSMutexCreate(INT8U prio, INT8U *err);
void OSMutexPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
INT8U OSMutexAccept(OS_EVENT *pevent, INT8U *err);
INT8U OSMutexPost(OS_EVENT *pevent);
OS_EVENT *OSMutexDel(OS_EVENT*, INT8U opt, INT8U *err);
INT8U OSMutexQuery(OS_EVENT*, OS_MUTEX_DATA*);

19

6.6 多事件同步机制

如果某个任务需要等待多个事件都到来才能继续执行,则需要使用到多事件管理机制。uc/OS-II提供了事件组标志来实现多事件管理。

6.6.1 Flag基本原理

uC/OS-II提供事件组标志实现多事件管理。Flag只是使用0/1来表示某个事件是否发生过,而不能直接被用来传递数据和消息。可以选择性地设置一个Flag最多可以管理的任务同步状态。主要包含在C源文件OS_FLAG.C中。 20

6.6.2 Flag数据结构

FCB结构体

typedef struct os_flag_grp{

          INT8U OSFlagType;                        //事件类型

          void *OSFlagWaitList;                      //指向等待的任务链表

          OS_FLAGS OSFlagFlags;               //信号列表

          INT8U OSFlagName[OS_FLAG_NAME_SIZE];

    }OS_FLAG_GRP;

事件标志等待链表结点

typedef struct os_flag_node{

          void *OSFlagNodeNext;

          void *OSFlagNodePrev;

          void *OSFlagNodeTCB;

          void *OSFlagNodeFlagGrp;          //指向此任务所等待的事件组标志

          OS_FLAGS OSFlagNodeFlags;     //等待的事件

          INT8U OSFlagNodeWaitType;       //等待方式

    }OS_FLAG_NODE;

6.6.3 Flag管理函数

INT8U wait_type, INT16U timeout, INT8U *err);
static void OS_FlagBlock(OS_FLAG_GRP *pgrp,OS_FLAG_NODE *pnode,OS_FLAGS flags,INT8U wait_type, INT16U timeout);     //类似于:OS_EventTaskWait();
void OS_FlagUnlink(OS_FLAG_NODE *pnode);   //等待超时删除结点,类似于:OS_EventTO();
OS_FLAGS OSFlagAccept(OS_FLAG_GRP *pgrp, OS_FLAGS flags,INT8U wait_type,INT8U *err);
OS_FLAGS OSFlagPost(OS_FLAG_GRP *pgrp,OS_FLAGS flags,INT8U opt,INT8U *err);
static BOOLEAN OS_FLAGTaskRdy(OS_FLAG_NODE *pnode,OS_FLAGS flags_rdy);
OS_FLAG_GRP *OSFlagDel(OS_FLAG_GRP*, INT8U opt, INT8U *err);
OS_FLAGS OSFlagPendGetFlagsRdy(void);    //获取任务就绪标志
OS_FLAGS OSFlagQuery(OS_FLAG_GRP*, INT8U *err);
INT8U OSFlagNameGet(OS_FLAG_GRP*, INT8U *pname, INT8U *err);
void OSFlagNameSet(OS_FLAG_GRP*, INT8U *pname, INT8U *err);

21

7.uc/OS-II内存分区管理

uC/OS-II根据需要将内存空间分成多个内存分区,每个内存分区由具有相同大小的内存块(Block)组成。主要包含在C源文件OS_MEM.C中。

7.1 内存控制块

OS_EXT OS_MEM OSMemTbl[OS_MAX_MEM_PART];
OS_EXT OS_MEM *OSMemFreeList;
typedef struct os_men{

        void *OSMemAddr;                 //首地址

        void *OSMemFreeList;            //分区中空闲空间的起始地址

        INT32U OSMemBlkSize;         //分区中块成员空间大小

        INT32U OSMemNBlks;           //块结构数量

        INT32U OSMemNFree;           //剩余空闲块个数

        INT8U OSMemName[];

}OS_MEM;

22

6.2 memPart管理函数

OS_MEM *OSMemCreate(void *addr, INT32U nblks,INT32U blksize, INT8U *err);
void *OSMemGet(OS_MEM *pmem, INT8U *err); //申请内存分区块
INT8U OSMemPut(OS_MEM *pmem, void *pblk); //释放内存分区块
INT8U OSMemQuery(OS_MEM *pmem, OS_MEM_DATA*);
INT8U OSMemNameGet(OS_MEM *pmem, INT8U *pname, INT8U *err);
INT8U OSMemNameSet(OS_MEM *pmem, INT8U *pname, INT8U *err);

23

8.一些问题

ustcanycall commented 10 years ago

9.参考资料