第5章任务的同步与通信第5章目录1、任务间的同步和事件控制块2、信号量及其操作3、互斥型信号量和任务优先级反转4、消息邮箱及其操作5、消息队列及其操作page第5章任务的同步与通信系统中的多个任务在运行时,经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的限制和制约,才保证任务的顺利运行。因此,操作系统必须具有对任务的运行进行协调的能力,从而使任务之间可以无冲突、流畅地同步运行,而不致导致灾难性的后果。与人们依靠通信来互相沟通,从而使人际关系和谐、工作顺利的做法一样,计算机系统是依靠任务之间的良好通信来保证任务与任务的同步的。例如,两个任务:任务A和任务B,它们需要通过访问同一个数据缓冲区合作完成一项工作,任务A负责向缓冲区写入数据,任务B负责从缓冲区读取该数据。显然,当任务A还未向缓冲区写入数据时(缓冲区为空时),任务B因不能从缓冲区得到有效数据而应该处于等待状态,只有等任务A向缓冲区写入了数据之后,才应该通知任务B去取数据。例如,任务A和任务B共享一台打印机,如果系统已经把打印机分配给了任务A,则任务B因不能获得打印机的使用权而应该处于等待状态,只有当任务A把打印机释放后,系统才能唤醒任务B使其获得打印机的使用权。如果这两个任务不这样做,那么也会造成极大的混乱。总之,多个任务共享同一资源或有工作顺序要求时,在正式工作之前要互相打招呼。page事件任务间的同步依赖于任务间的通信。在μC/OS-II中,是使用信号量、邮箱(消息邮箱)和消息队列这些被称作事件的中间环节来实现任务之间的通信的。宋丹丹黄宏page5第5章任务的同步与通信一、任务间的同步鉴于任务间直接制约或间接制约性的关系,这种制约性的合作运行机制叫做任务的同步。二、事件汉语中所谓的“事件”,是指一个事情的发生。在uC/OS-II中将信号量、消息邮箱和消息队列的一个存在称为一个事件,事件操作:创建、发送、请求和删除等。(uC/OS的原作者将“事件---EVENT”理解为静态的数据结构。)page61.信号量互斥型信号量:通常表现为一个二值型信号,用一位二进制位来表示(1/0),可以实现共享资源的独占式占用。计数型信号量:用以“多个同类型资源”的管理,通常用一个计数器实现(例如:存储块)。page信号量应用举例例5-1(参看源代码)加全局变量ac_key来作为信号量例5-2(参看源代码)72020/4/29page82.消息邮箱用于解决任务间的数据传送问题。例如:Task_A采集一个数据,Task_B要使用Task_A采集的数据。在多任务OS中采用消息传送的方式实现任务间的“单批次数据”通信,这个数据称为“消息”。原理:在内存中创建数据传送缓冲区(消息缓冲区),通过传送该缓冲区的地址指针传递数据。这个缓冲区指针的数据结构称为“消息邮箱”。page92.消息邮箱指向保存任务间传递信息的存储空间(缓存区)的指针结构称为消息邮箱。Task_APointer(消息邮箱)Task_BMessageBuffer(消息缓冲区)Task_A通过指针将消息写入到缓冲区(发送消息)Task_B通过指针将消息从缓冲区读出(请求消息)page消息邮箱示例例5-3(参教材源代码P125)102020/4/29page113.消息队列若消息邮箱被定义成拥有若干个元素的数组,用来传递多个消息的地址指针,这样消息的数据结构称为消息队列。用于解决任务间的“多个数据”传送问题。在多任务OS中,采用“指针数组”的方式进行多数据的传送。这个指向“指针数组”的指针+“指针数组”+消息缓冲区所构成的数据结构称为“消息队列”。page5.2事件控制块及事件处理函数西安邮电学院计算机系122020/4/29page132020/4/291.事件的等待任务队列当一个“事件”被占用时,其它请求该事件的任务暂时得不到事件的服务,处于等待状态。ucos用了与任务就绪表类似的位图,即定义了一个INT8U类型的数组OSEventTbl[]作为等待任务的记录表(等待任务表)来管理“事件”;即对那些等待该事件的各个任务进行管理(记录等待该事件的任务并排序,任务等待事件有限时等)。每个事件都有一个“等待任务表”,用于完成事件对任务的驱动、限时等管理,其原理类似于任务就续表。任务等待事件限时则记录在TCB的OSTCBDly成员中,每个Tick都会对其进行维护,当限时到时uC/OS-II强行将其转入就续状态。page141.事件的等待任务队列page15三、事件控制块事件控制块的结构OSEventPtr指针,只有在所定义的事件是邮箱或者消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构。typedefstruct{INT8UOSEventType;/*事件类型*/INT16UOSEventCnt;/*计数器(当事件是信号量时)*/void*OSEventPtr;/*指向消息或者消息队列的指针*/INT8UOSEventGrp;/*等待任务所在的组*/INT8UOSEventTbl[OS_EVENT_TBL_SIZE];/*等待任务列表*/}OS_EVENT;page16.OSEventTbl[]/.OSEventGrp与前面提过的OSRdyTbl[]和OSRdyGrp非常相像,只不过前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。.OSEventCnt:当事件是一个信号量时,.OSEventCnt是用于信号量的计数器。.OSEventType:定义了事件的具体类型。它可以是信号量(OS_EVENT_SEM)、邮箱(OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。用户要根据该域的具体值来调用相应的系统函数,以保证对其进行的操作的正确性。page西安邮电学院计算机系172020/4/29事件控制块(ECB)的结构OSEventType的值说明OS_EVENT_TYPE_SEM信号量事件_MUTEX互斥型信号量_MBOX消息邮箱事件_Q消息队列事件_UNUSED未用(空)ECBOSEventType取值含义OSEventTypeOSEventCntOSEventPtrOSEventGrp1/0peventOSEventTbl[]任务等待表page18四、事件控制块的基本操作函数1.事件控制块的初始话函数函数作用:把变量OSEventGrp及任务等待表中的每一位都清0,即令事件的任务等代表中不含有任何等待任务。被OSXXXCreate()调用(XXX代表不同的事件,参看P132表5-2)voidOSEventWaitListInit(OS_EVENT*pevent){INT8UI;pevent-OSEventGrp=0x00;for(i=0;iOS_EVENT_TBL_SIZE;i++){pevent-OSEventTbl[i]=0x00;}}page192.使一个任务进入等待状态的函数当一个任务在请求一个事件而不能获得时,应把本任务登记在等待任务列表中,并把任务控制块中的任务状态置为等待状态和把任务置为非就绪任务,被OSXXXPend()调用voidOSEventTaskWait(OS_EVENT*pevent){OSTCBCur-OSTCBEventPtr=pevent;(1)if((OSRdyTbl[OSTCBCur-OSTCBY]&=~OSTCBCur-OSTCBBitX)==0){(2)任务就绪表相应位清0OSRdyGrp&=~OSTCBCur-OSTCBBitY;}pevent-OSEventTbl[OSTCBCur-OSTCBY]|=OSTCBCur-OSTCBBitX;(3)事件等待任务表相应位置位pevent-OSEventGrp|=OSTCBCur-OSTCBBitY;}page203.正在等待的任务进入就绪状态该函数的作用是在任务等待表中查看优先级最高的任务,将其从任务等待表中相应位清0,然后将其在任务就绪表中相关位设为就绪状态,被OSXXXPost()调用.voidOSEventTaskRdy(OS_EVENT*pevent,void*msg,INT8Umsk){OS_TCB*ptcb;INT8Ux;INT8Uy;INT8Ubitx;INT8Ubity;INT8Uprio;……}page214.等待超时的任务转为就绪态正在等待事件的任务在预先指定的时间内仍然没有获取事件,这时需调用此函数使其进入就绪状态。被OSXXXPend()调用voidOSEventTO(OS_EVENT*pevent){if((pevent-OSEventTbl[OSTCBCur-OSTCBY]&=~OSTCBCur-OSTCBBitX)==0){pevent-OSEventGrp&=~OSTCBCur-OSTCBBitY;}OSTCBCur-OSTCBStat=OS_STAT_RDY;OSTCBCur-OSTCBEventPtr=(OS_EVENT*)0;}page空事件控制块链表在μC/OS-II初始化时,系统会在初始化函数OSInit()中按应用程序使用事件的总数OS_MAX_EVENTS(在文件OS_CFG.H中定义),创建OS_MAX_EVENTS个空事件控制块并借用成员OSEventPtr作为链接指针,把这些空事件控制块链接成一个单向链表。由于链表中的所有控制块尚未与具体事件相关联,故该链表叫做空事件控制块链表。以后,每当应用程序创建一个事件时,系统就会从链表中取出一个空事件控制块,并对它进行初始化以描述该事件。而当应用程序删除一个事件时,就会将该事件的控制块归还给空事件控制块链表page信号量及其操作在使用信号量之前,应用程序必须调用函数OSSemCreate()来创建一个信号量,OSSemCreate()的原型为:OS_EVENT*OSSemCreate(INT16Ucnt);//cnt信号量计数器初值函数的返回值为已创建的信号量的指针。page24二、信号量的操作1.信号量的创建:OS_EVENT*OSSemCreate(INT16Ucnt){OS_EVENT*pevent;OS_ENTER_CRITICAL();pevent=OSEventFreeList;(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)}信号量创建page252.请求一个信号量,OSSemPend()/OSSemAccept()在信号量无效时准许任务不等待而继续运行,用下面函数INT16UOSSemAccept(OS_EVENT*PEVENT)//信号量指针任务通过调用函数OSSemPend()请求信号量,函数OSSemPend()的原型如下:voidOSSemPend(OS_EVENT*pevent,//信号量的指针INT16Utimeout,//等待时限INT8U*err);//错误信息(参看源代码OS_SEM.c文件)参数pevent是被请求信号量的指针。为防止任务因得不到信号量而处于长期的等待状态,函数OSSemPend允许用参数timeout设置一个等待时间的限制,当任务等待的时间超过timeout时可以结束等待状态而进入就绪状态。如果参数timeout被设置为0,则表明任务的等待时间为无限长。page263.发送一个信号量