1、嵌入式操作系统基础 第九章 任务的同步与通信1 第九章 任务的同步与通信u任务间的同步和事件控制块u信号量及其操作u任务优先级反转和互斥型信号量u消息邮箱及其操作u消息队列及其操作嵌入式操作系统基础 第九章 任务的同步与通信2第九章 任务的同步与通信一、任务间的同步 鉴于任务间直接制约或间接制约性的关系,这种制约性的合作运行机制叫做任务的同步。二、事件 用于uC/OS-II任务间通信媒介的信号量、邮箱和消息队列等数据结构会影响到任务的程序流程,这些通信媒介又被称为事件(Events)。9.1 任务间的同步和事件控制块嵌入式操作系统基础 第九章 任务的同步与通信31.信号量互斥型信号量:通常表现
2、为一个二值型信号,用一位二进制位来表示(1/0),可以实现共享资源的独占式占用。信号量:通常表现为一个递减的计数器信号,可以实现若干个同类资源的共享,提高资源使用效率。嵌入式操作系统基础 第九章 任务的同步与通信42.消息邮箱 指向保存任务间传递信息的存储空间(缓存区)的指针结构称为消息邮箱。嵌入式操作系统基础 第九章 任务的同步与通信53.消息队列 若消息邮箱被定义成拥有若干个元素的数组,用来传递多个消息的地址指针,这样消息的数据结构称为消息队列。4.事件的等待任务队列两个功能:v对等待事件的所有记录进行登记并排序v允许任务有一定的等待时间嵌入式操作系统基础 第九章 任务的同步与通信6嵌入式
3、操作系统基础 第九章 任务的同步与通信7三、事件控制块v事件控制块的结构vOSEventPtr指针,只有在所定义的事件是邮箱或者消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构。typedef struct void *OSEventPtr;/*指向消息或者消息队列的指针*/INT8U OSEventTblOS_EVENT_TBL_SIZE;/*等待任务列表 */INT16U OSEventCnt;/*计数器(当事件是信号量时)*/INT8U OSEventType;/*事件类型 */INT8U OSEventGrp;/*等待任务所在的
4、组 */OS_EVENT;嵌入式操作系统基础 第九章 任务的同步与通信8v.OSEventTbl/.OSEventGrp 与前面提过的OSRdyTbl和OSRdyGrp非常相像,只不过前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。v.OSEventCnt:当事件是一个信号量时,.OSEventCnt是用于信号量的计数器。v.OSEventType:定义了事件的具体类型。它可以是信号量(OS_EVENT_SEM)、邮箱(OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。用户要根据该域的具体值来调用相应的系统函数,以保证对其进行
5、的操作的正确性。嵌入式操作系统基础 第九章 任务的同步与通信9四、事件控制块的基本操作函数1.事件控制块的初始话函数 函数作用:把变量OSEventGrp及任务等待表中的每一位都清0,即令事件的任务等代表中不含有任何等待任务。void OSEventWaitListInit(OS_EVENT*pevent)INT8U i;pevent-OSEventGrp=0 x00;for(i=0;i OSEventTbli=0 x00;嵌入式操作系统基础 第九章 任务的同步与通信102.使一个任务进入等待状态的函数 当一个任务在请求一个事件而不能获得时,应把次任务登记在时间的等待任务列表中,并把任务控制块
6、中的任务状态置为等待状态和吧任务置为非就绪任务void OSEventTaskWait(OS_EVENT*pevent)OSTCBCur-OSTCBEventPtr=pevent;(1)if(OSRdyTblOSTCBCur-OSTCBY&=OSTCBCur-OSTCBBitX)=0)(2)OSRdyGrp&=OSTCBCur-OSTCBBitY;pevent-OSEventTblOSTCBCur-OSTCBY|=OSTCBCur-OSTCBBitX;(3)pevent-OSEventGrp|=OSTCBCur-OSTCBBitY;嵌入式操作系统基础 第九章 任务的同步与通信113.正在等待的
7、任务进入就绪状态 当一个正在等待的任务具备了可以运行的条件,这时需调用此函数来使它进入就绪状态。该函数的作用就是把调用这个函数的任务在任务等待表中的位置清O后,再把任务子啊任务就绪表中的对应位置1,然后引起一次任务调度。void OSEventTaskRdy(OS_EVENT*pevent,void*msg,INT8U msk)OS_TCB*ptcb;INT8U x;INT8U y;INT8U bitx;INT8U bity;INT8U prio;嵌入式操作系统基础 第九章 任务的同步与通信124.等待超时的任务转为就绪态 正在等待事件的任务在预先指定的时间内仍然没有获取事件,这时需调用此函数
8、来转换的它的状态。void OSEventTO(OS_EVENT*pevent)if(pevent-OSEventTblOSTCBCur-OSTCBY&=OSTCBCur-OSTCBBitX)=0)(1)pevent-OSEventGrp&=OSTCBCur-OSTCBBitY;OSTCBCur-OSTCBStat =OS_STAT_RDY;(2)OSTCBCur-OSTCBEventPtr=(OS_EVENT*)0;(3)嵌入式操作系统基础 第九章 任务的同步与通信13五、空事件控制块的的组织 和任务控制块的组织结构类似:所有的事件控制块也被组织成两个链表,当系统初始化时,通过控制块指针OS
9、EventPtr把所有的空时间控制块链接成一个空事件控制块链表。嵌入式操作系统基础 第九章 任务的同步与通信149.1 信号量及其操作一、信号量 由16位的无符号整数(0 到65,535之间)构成的信号量计数器和任务等待表两部分组成。计数器决定共享资源的任务数。嵌入式操作系统基础 第九章 任务的同步与通信15二、信号量的操作1.信号量的创建:信号量一旦建立就不能删除了,因此也就不可能将一个已分配的任务控制块再放回到空闲ECB链表中。OS_EVENT*OSSemCreate(INT16U cnt)OS_EVENT*pevent;OS_ENTER_CRITICAL();pevent=OSEvent
10、FreeList;(1)if(OSEventFreeList!=(OS_EVENT*)0)(2)OSEventFreeList=(OS_EVENT*)OSEventFreeList-OSEventPtr;OS_EXIT_CRITICAL();if(pevent!=(OS_EVENT*)0)(3)pevent-OSEventType=OS_EVENT_TYPE_SEM;(4)pevent-OSEventCnt =cnt;(5)OSEventWaitListInit(pevent);(6)return(pevent);(7)信号量创建嵌入式操作系统基础 第九章 任务的同步与通信162.等待一个信号
11、量,OSSemPend()/OSSemAccept()void OSSemPend(OS_EVENT*pevent,/信号量指针 INT16U timeout,/等待时限 INT8U*err);/错误信息 INT16U OSSEMAccept(OS_EVENT*PEVENT/信号量指针 )3.发送一个信号量发送一个信号量,OSSemPost()()任务获得信号量,并在访问共享资源结束以后,必须释放信号量,此过程需调用函数OSSemPost()完成。嵌入式操作系统基础 第九章 任务的同步与通信17INT8U OSSemPost(OS_EVENT*pevent)OS_ENTER_CRITICAL(
12、);if(pevent-OSEventType!=OS_EVENT_TYPE_SEM)(1)OS_EXIT_CRITICAL();return(OS_ERR_EVENT_TYPE);if(pevent-OSEventGrp)(2)OSEventTaskRdy(pevent,(void*)0,OS_STAT_SEM);(3)OS_EXIT_CRITICAL();OSSched();(4)return(OS_NO_ERR);else if(pevent-OSEventCnt OSEventCnt+;(5)OS_EXIT_CRITICAL();return(OS_NO_ERR);else OS_EX
13、IT_CRITICAL();return(OS_SEM_OVF);嵌入式操作系统基础 第九章 任务的同步与通信183.发送一个信号量发送一个信号量,OSSemPost()()OS_EVENT*OSSemDel(OS_EVENT*pevent,INT8U opt,INT8U*err );3.信号量状态查询信号量状态查询,OSSemQuery()()INT8U OSSemQuery(OS_EVENT*pevent,OS_SEM_DATA*pdata );Typedef structINT16U OSCnt;INTU8U OSEventb1OS_EVENT_SIZE;INT8U OSEventGrp
14、;OS_SEM_DATA;嵌入式操作系统基础 第九章 任务的同步与通信199.3 任务优先级反转和互斥型信号量一、任务优先级的反转现象 在可剥夺型内核中,当任务以独占方式使用共享资源时,会出现低优先级任务先于高优先级任务而被运行的现象,这种现象就成为任务优先级反转。嵌入式操作系统基础 第九章 任务的同步与通信20void TaskA(void)SetTaskPriority(RES_X_PRIO);/访问共享资源X SetTaskPriority(TASK_A_PRIO);形成原因:使用信号量的任务是否能够运行时受任务的优先级别以及是否占用信号量两个条件约束的,而信号量的约束高于优先级别的约束
15、。解决方案:一旦获取信号量的任务投入运行,其将暂用最高优先级别,直至任务执行完成。嵌入式操作系统基础 第九章 任务的同步与通信21二、互斥型信号量v实现对共享资源的独占式处理。v解决任务的优先级反转:变量OSEventPtr占16位,低8位作为信号量有效性的判断位;高8位则存放任务运行后临时暂用的优先级别。嵌入式操作系统基础 第九章 任务的同步与通信221.互斥型信号量的创建OS_EVENT*OSMutexCreate(INT8U prio,INT8U*err );2.请求互斥型信号量:OSMutexPend()/OSMutexAccept()OS_EVENT*OSMutexPend(OS_E
16、VENT*pevent,INT16U Timeout,INT8U*err );嵌入式操作系统基础 第九章 任务的同步与通信233.发送互斥型信号量INT8U OSMutexQuery(OS_EVENT*pevent );4.查询互斥型信号量的当前状态OS_EVENT*OSMutexPend(OS_EVENT*pevent,OS_MUTEX_DATA*pdata );Typedef structINT8U OSValue;INTU8U OSEvenTb1OS_EVENT_SIZE;INT8U OSEventGrp;INT8U OSOwnerPrio;INT8U OSMutexPIP;OS_MUT
17、EX_DATA;嵌入式操作系统基础 第九章 任务的同步与通信245.删除互斥型信号量OS_EVENT*OSMutexPend(OS_EVENT*pevent,INT8U opt,INT8U*err );嵌入式操作系统基础 第九章 任务的同步与通信259.4 消息邮箱及其操作一、消息邮箱 任务与任务间要传递一个数据,为了适应不同数据的需要,最好在存储中建立一个数据缓冲区,把要传递的数据放在该缓冲区中,从而实现任务间的数据通信。嵌入式操作系统基础 第九章 任务的同步与通信26二、消息邮箱的操作OS_EVENT*OSMboxCreate(void*msg)OS_EVENT*pevent;OS_ENT
18、ER_CRITICAL();pevent=OSEventFreeList;if(OSEventFreeList!=(OS_EVENT*)0)OSEventFreeList=(OS_EVENT*)OSEventFreeList-OSEventPtr;OS_EXIT_CRITICAL();if(pevent!=(OS_EVENT*)0)pevent-OSEventType=OS_EVENT_TYPE_MBOX;(1)pevent-OSEventPtr =msg;(2)OSEventWaitListInit(pevent);return(pevent);(3)创建邮箱1.邮箱的创建嵌入式操作系统基础
19、 第九章 任务的同步与通信272.发送一个消息到邮箱中:OSMboxPost()/OSMboxPostOpt()INT8U OSMboxPost(OS_EVENT*pevent,void*msg )INT8U OSMboxPostOpt(OS_EVENT*pevent,void*msg,INT8U opt )嵌入式操作系统基础 第九章 任务的同步与通信283.等待一个邮箱中的消息:OSMboxPend()/OSMboxAccept()void *OSMboxPend(OS_EVENT*pevent,INT16U timeout,INT8U*err )void *OSMboxAccept(OS_
20、EVENT*pevent )嵌入式操作系统基础 第九章 任务的同步与通信294.查询一个邮箱的状态查询一个邮箱的状态,OSMboxQuery,OSMboxQuery()()INT8U OSMboxQuery(OS_EVENT*pevent,OS_MBOX_DATA*pdata )Typedef structvoid*OSMsg;INTU8U OSEvenTb1OS_EVENT_TBL_SIZE;INT8U OSEventGrp;OS_MBOX_DATA;5.删除删除邮箱邮箱,OSMboxDel,OSMboxDel()()OS_EVENT *OSMboxDel(OS_EVENT*pevent,I
21、NT8U opt INT8U*err )嵌入式操作系统基础 第九章 任务的同步与通信309.5 消息队列及其操作一、消息队列 消息队列可以传递多条消息,通常由三部分组成:事件控制块、消息对列和消息.嵌入式操作系统基础 第九章 任务的同步与通信311.消息指针数组 v消息指针数组结构嵌入式操作系统基础 第九章 任务的同步与通信32vOSQPtr在空闲队列控制块中链接所有的队列控制块。一旦建立了消息队列,该域就不再有用了。vOSQStart是指向消息队列的指针数组的起始地址的指针。用户应用程序在使用消息队列之前必须先定义该数组。vOSQEnd是指向消息队列结束单元的下一个地址的指针。该指针使得消息
22、队列构成一个循环的缓冲区。vOSQIn是指向消息队列中插入下一条消息的位置的指针。当OSQIn和.OSQEnd相等时,OSQIn被调整指向消息队列的起始单元。嵌入式操作系统基础 第九章 任务的同步与通信33vOSQOut是指向消息队列中下一个取出消息的位置的指针。当OSQOut和OSQEnd相等时,OSQOut被调整指向消息队列的起始单元。vOSQSize是消息队列中总的单元数。该值是在建立消息队列时由用户应用程序决定的。在C/OS-II中,该值最大可以是65,535。vOSQEntries是消息队列中当前的消息数量。当消息队列是空的时,该值为0。当消息队列满了以后,该值和OSQSize值一样
23、。在消息队列刚刚建立时,该值为0。嵌入式操作系统基础 第九章 任务的同步与通信34 系统把消息指针数组的基本参数都记录在一个叫做队列控制块(OS_Q)的结构中。并用队列控制块中的指针OSQPtr将所有队列控制块链接为链表。2.队列控制块 嵌入式操作系统基础 第九章 任务的同步与通信35一、消息队列的操作1.消息队列的创建,OSQCreate()OS_EVENT*OSQCreate(void*start,INT16U size )2.等待一个消息队列中的消息,OSQPend()/OSQAccept()void*OSQPend(OS_EVENT*pevent,INT16U timeout,INT8
24、U*err )void*OSQAccept(OS_EVENT*pevent )嵌入式操作系统基础 第九章 任务的同步与通信363.向消息队列发送一个消息(FIFO),OSQPost()和OSQPostOpt()INT8U OSQPost(OS_EVENT*pevent,void*msg )INT8U OSQPostOpt(OS_EVENT*pevent,void*msg.INT8U opt )嵌入式操作系统基础 第九章 任务的同步与通信374.清空一个消息队列,OSQFlush()INT8U OSQFlush(OS_EVENT*pevent)OS_Q *pq;OS_ENTER_CRITICAL
25、();if(pevent-OSEventType!=OS_EVENT_TYPE_Q)(1)OS_EXIT_CRITICAL();return(OS_ERR_EVENT_TYPE);pq =pevent-OSEventPtr;pq-OSQIn =pq-OSQStart;(2)pq-OSQOut =pq-OSQStart;pq-OSQEntries=0;OS_EXIT_CRITICAL();return(OS_NO_ERR);嵌入式操作系统基础 第九章 任务的同步与通信385.删除一个消息队列,OSQDEL()OS_EVENT OSQDel(OS_EVENT*pevent );6.查询消息队列,OSQQuery()OS_EVENT OSQQuery(OS_EVENT*pevent,OS_Q_DATA*pdata );Typedef structvoid*OSMsg;INTU8U OSEvenTb1OS_EVENT_TBL_SIZE;INT8U OSEventGrp;INT16U OSQSize;INT16U OSNMsgs;OS_FLAG_GRP;