事件:μC/OS-II使用信号量,消息邮箱,消息队列这些中间环节来实现任务之间的通信,它们统称“事件”
事件控制块:μC/OS-II使用数据结构OS_EVENT来描述信号量,消息邮箱,消息队列这些事件。
typedef struct os_event
{
INT8U OSEventType; //事件类型
void *OSEventPtr; //消息邮箱或消息队列的指针
INT16U OSEventCnt; //信号量计数器
OS_PRIO OSEventGrp; //等待事件的任务组
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; //等待任务表
} OS_EVENT;
当有任务申请信号量时:
μC/OS-II中与信号量相关的函数定义在os_sem.c文件中,函数声明在ucos_ii.h文件中,以下列出函数声明以供查阅。
OS_EVENT *OSSemCreate (INT16U cnt);
void OSSemPend (OS_EVENT *pevent,
INT32U timeout, //赋值0则时间无限长
INT8U *perr);
INT16U OSSemAccept (OS_EVENT *pevent);
INT8U OSSemPost (OS_EVENT *pevent);
OS_EVENT *OSSemDel (OS_EVENT *pevent,
INT8U opt,
INT8U *perr);
INT8U OSSemQuery (OS_EVENT *pevent,
OS_SEM_DATA *p_sem_data);
void OSSemSet (OS_EVENT *pevent,
INT16U cnt,
INT8U *perr);
互斥信号量:互斥信号量是一个二值信号量,它除了具有普通信号量的机制外,主要用来解决优先级反转问题。
优先级反转:当任务以独占方式使用共享资源时,会出现低优先级任务先于高优先级任务的现象。例如:任务A,B,C的优先级为 A > B > C ,任务A,C使用同一个共享资源S。假设CPU在执行任务C在使用共享资源S,此时任务A剥夺CPU使用权,但是,由于在任务C中没有发送信号量,所以在任务A中无法访问共享资源S而进入等待状态,那么CPU继续执行任务C。这时候,如果任务B剥夺了CPU使用权,就造成了一个现象:任务A的优先级 > 任务B的优先级,但是任务A却需要等待任务B执行结束,任务C再执行过程且发送信号量后,任务A才能执行,似乎任务B的优先级 > 任务A的优先级。
μC/OS-II中解决优先级反转的方法:使获得信号量的任务(任务C)的优先级别在使用共享资源S期间暂时提升到一个更高的优先级,使该任务不被比任务A优先级低且比任务C优先级高的任务B(B1,B2,B3多个任务)打断。而这个“更高的优先级”不能是其他已有任务的优先级(因为μC/OS-II不允许多个任务具有有相同的优先级),并且优先级要比任务B(B1,B2,B3多个任务)的优先级高,比任务A的优先级低。
互斥信号量的成员OSEventCnt被分为高8位和低8位,高8位用来存放解决优先级反转问题而要提升至的优先级,低8位用来存放信号值(0xFF时信号有效,否则信号无效)。至于μC/OS-II具体是如何解决优先级反转问题的,可以查看OSMutexPend()和OSMutexPost()函数的源码。
μC/OS-II中与互斥信号量相关的函数定义在os_mutex.c文件中,函数声明在ucos_ii.h文件中,以下列出函数声明以供查阅。
OS_EVENT *OSMutexCreate (INT8U prio,
INT8U *perr);
void OSMutexPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr);
BOOLEAN OSMutexAccept (OS_EVENT *pevent,
INT8U *perr);
INT8U OSMutexPost (OS_EVENT *pevent);
OS_EVENT *OSMutexDel (OS_EVENT *pevent,
INT8U opt,
INT8U *perr);
INT8U OSMutexQuery (OS_EVENT *pevent,
OS_MUTEX_DATA *p_mutex_data);
任务与任务之间常常需要通过传递数据的方式进行通信,为了达到这个目的,可以在内存中创建一个存储空间作为数据缓冲区,然后在任务与任务之间传递数据缓冲区的指针。事件控制块的成员OSEventPtr指向这个缓冲区,这种用来传递数据缓冲区指针的数据结构就是消息邮箱。
μC/OS-II中与消息邮箱相关的函数定义在os_mbox.c文件中,函数声明在ucos_ii.h文件中,以下列出函数声明以供查阅。
OS_EVENT *OSMboxCreate (void *pmsg);
void *OSMboxPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr);
void *OSMboxAccept (OS_EVENT *pevent);
INT8U OSMboxPost (OS_EVENT *pevent,
void *pmsg);
OS_EVENT *OSMboxDel (OS_EVENT *pevent,
INT8U opt,
INT8U *perr);
INT8U OSMboxQuery (OS_EVENT *pevent,
OS_MBOX_DATA *p_mbox_data);
消息邮箱只能用来传递一个消息,而可以用来传递一组消息的数据结构叫做消息队列。消息队列由三部分组成:事件控制块,消息队列控制块,消息。事件控制块的成员OSEventPtr指向消息队列控制块(OS_Q),消息队列控制块管理着一个数组( MsgTbl [ ] ),该数组中的元素都是指向消息的指针。
μC/OS-II中与消息队列相关的函数定义在os_q.c文件中,函数声明在ucos_ii.h文件中,以下列出函数声明以供查阅。
OS_EVENT *OSQCreate (void **start,
INT16U size);
void *OSQPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr);
void *OSQAccept (OS_EVENT *pevent,
INT8U *perr);
INT8U OSQPost (OS_EVENT *pevent,
void *pmsg);
INT8U OSQPostFront (OS_EVENT *pevent,
void *pmsg);
INT8U OSQPostOpt (OS_EVENT *pevent,
void *pmsg,
INT8U opt);
OS_EVENT *OSQDel (OS_EVENT *pevent,
INT8U opt,
INT8U *perr);
INT8U OSQQuery (OS_EVENT *pevent,
OS_Q_DATA *p_q_data);
INT8U OSQFlush (OS_EVENT *pevent);
在实际应用中,任务常常需要根据多个信号量组成作用的结果来决定任务的运行方式,为此,μC/OS-II提供了信号量集。信号量集可以分为两个部分:标志组和等待任务链表。
//信号量集的标志组
typedef struct os_flag_grp
{
INT8U OSFlagType; //识别信号量集的标志
void *OSFlagWaitList; //指向等待任务链表的指针
OS_FLAGS OSFlagFlags; //输入信号量值列表
} OS_FLAG_GRP;
//信号量集的等待任务节点,组成等待任务链表
typedef struct os_flag_node
{
void *OSFlagNodeNext; //指向下一个节点的指针
void *OSFlagNodePrev; //指向上一个节点的指针
void *OSFlagNodeTCB; //指向对应任务控制块的指针
void *OSFlagNodeFlagGrp; //反向指向信号量集的指针
OS_FLAGS OSFlagNodeFlags; //等待的信号量
INT8U OSFlagNodeWaitType; //逻辑关系
//OS_FLAG_WAIT_AND
//OS_FLAG_WAIT_ALL
//OS_FLAG_WAIT_OR
//OS_FLAG_WAIT_ANY
} OS_FLAG_NODE;
μC/OS-II中与消息队列相关的函数定义在os_flag.c文件中,函数声明在ucos_ii.h文件中,以下列出函数声明以供查阅。
OS_FLAG_GRP *OSFlagCreate (OS_FLAGS flags,
INT8U *perr);
OS_FLAGS OSFlagPend (OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U wait_type,
INT32U timeout,
INT8U *perr);
OS_FLAGS OSFlagAccept (OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U wait_type,
INT8U *perr);
OS_FLAGS OSFlagPost (OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U opt,
INT8U *perr);
//该函数可以根据信号量集的不同状态实现不同的功能
OS_FLAGS OSFlagQuery (OS_FLAG_GRP *pgrp,
INT8U *perr);
OS_FLAG_GRP *OSFlagDel (OS_FLAG_GRP *pgrp,
INT8U opt,
INT8U *perr);
本文主要介绍了μC/OS-II中任务的同步与通信,根据形式的不同,可以分为信号量,互斥信号量,消息邮箱,消息队列,信号量集。在任务之间需要通信的时候,根据它们的特点来选择。