1:分析OS_EVENT数据结构在Mutex、Sem、Mailbox、Queue四种不同机制下,各个成员的含义;
OS_EVENT数据结构:
typedef struct os_event {
INT8U OSEventType;
void *OSEventPtr;
INT16U OSEventCnt;
OS_PRIO OSEventGrp;
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; } OS_EVENT;
参数 | Mutex | Sem | Mailbox | Queue |
OSEventType | 用于记录当前event是四种机制中的哪一种 | |||
OSEventPtr | - | - | 是一个指针用于指向要传递的内容的地址 | |
OSEventCnt | 记录任务优先级的一个变量 | 一个计数值 | 不使用 | 不使用 |
OSEventGrp | 等待事件发生的任务分组 | |||
OSEventTbl | 等待事件发生的任务列表 |
2:对Mutex和Sem的Pend、Post实现进行代码分析;
Mutex和Sem机制Pend和Post的两个操作,这两个操作是成对使用的。
pend主要功能:检查create的event中的OSEventCnt是否大于0,如果大于0,说明需要保护的资源还允许任务使用,这时候只需要将OSEventCnt减1操作,表示又有一个任务占用了资源的使用权;如果OSEventCnt不大于0,则说明任务可以使用的资源的权限已经达到上限,这时候就要把要使用的该资源的任务挂起(1),让其处于等待状态,并把任务放到event的event group和event table中,处于等待状态的任务只能等其他任务释放对资源的使用权之后才可以继续运行。
post的主要功能:要释放被占用的资源的使用权,其操作首先会检查当前event的group和table中有没有在等待当前event的任务存在,如果有使用权直接转给等待的任务(1),不需要对OSEventCnt加操作,如果没有等待当前event的任务,则只需要将OSEventCnt加1操作,说明资源的可使用权又大了一个。
Pend和Post的代码流程分析:
Pend:
OSSemPend函数首先判断当前的任务操作是否是一个sem event;然后判断是否中断中,如果是则返回错误;之后检查该sem event的资源限号量是否大于0,如果是则进行减操作表示信号量被一个任务占用,如果不大于0,说明对改event来说,已经没有信号量供使用需要的操作是挂起当前任务,并调用任务切换函数,在上面的代码中在OS_Sched()上面的四步操作是挂起任务操作,直到有可以使用的信号量再次执行任务切换时,会切换到当前任务继续从OS_Sched开始执行,可以看出下面的操作是从event的等待group和table中删除该任务,并且改变任务的状态为OS_STAT_RDY。
Post:
Post函数实现相对简单,在判断完当前操作是否是sem event之后,就判断当前的event等待group中是否有等待该event的任务,如果有就设置等待该event的任务拥有该信号量的使用权限设置改任务处于RDY状态然后执行任务调度,让等待的任务得以执行,在此有一个巧妙地设计就是对于信号量的计量值OSEventCnt不做操作,因为如果event等待group中有等待任务的话,意味着在此释放信号量,等待任务就获得了信号量。如果在等待group中没有等待任务的话,就会给OSEventCnt做加操作,表示有一个任务释放了该信号量。在此说明的是该函数主要的操作在OS_EventTaskRdy中,其主要功能就是把event的等待group和table中的任务解析出来放到任务Rdy列表中,并把event中的等待任务从列表中删除。
3:并对比分析标志组机制从API接口、功能上同上述四个通信机制的差异;
- 事件标志组数据结构
事件标志组的主要数据结构包括事件标志组、事件标志节点、事件标志组实体、链表等。
事件标志组OS_FLAG_GRP
事件标志组控制块是事件的最重要的数据结构之一,其定义如下所示。
typedef struct os_flag_grp {
INT8U OSFlagType; //表示该事件标志组是否使用
void *OSFlagWaitList; //事件节点指针,指向事件组的第一个事件标志节点
OS_FLAGS OSFlagFlags; //该成员可以根据配置,可以配置为8、16、32位数据。在ucos_ii.h中配置OS_FLAGS_NBITS宏来选择OS_FLAGS数据类型的位数。改数据的每一位都表示一个事件,哪一位表示哪一个事件,由用户自己定义。8位的数据可以表示8种事件的组合,16位的数据可以表示16种事件的组合。某位为1或0表示某一种事情已经发生了,具体由用户自己设定。
INT8U *OSFlagName; //事件标志组名称
} OS_FLAG_GRP;
事件标志节点OS_FLAG_NODE
事件标志节点包含等待事件标志的任务的信息。一个阻塞任务对应一个事件标志组节点。通过事件标志节点是否为空可以判断该事件标志组是否为有任务等待。
起定义为:
typedef struct os_flag_node {
void *OSFlagNodeNext; //指向事件标志节点链表的后一个节点
void *OSFlagNodePrev; //指向事件标志节点链表的前一个节点
void *OSFlagNodeTCB; //指向等待任务的TCB
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;
事件标志组的实体
#define OS_MAX_FLAGS 5 (os_cfg.h)
OS_FLAG_GRP OSFlagTbl[OS_MAX_FLAGS]; (ucos_ii.h)
系统默认标志组的实体个数是5个,可以根据自己的需求进行配置。
事件标志组空闲链表:
所有空闲链表标志组控制块都链接为一个单链表。全局变量指针OSFlagFreeList指向该链表表头。在系统运行前初始化的时候,所有的事件标志组控制块都挂在空闲链表上。
- 事件标志组初始化OS_FlagInit
事件标志组初始化函数,跟信号量的初始化没有什么太大的区别。其主要功能是对所有事件标志组OS_FLAG_NODE进行初始化,
同时将所有事件标志组控制块链接成单链表,并OSFlagFreeList指向该链表。具体代码不再分析。