第4章 任务的同步及通信_第1页
第4章 任务的同步及通信_第2页
第4章 任务的同步及通信_第3页
第4章 任务的同步及通信_第4页
第4章 任务的同步及通信_第5页
已阅读5页,还剩46页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、1嵌入式操作系统原理2第四章:任务的同步与通信目标:本章旨在向学员介绍任务的同步机制及通信方式,通过本章的学习,学员应该掌握如下知识: 事件控制块的概念 信号量及其使用 消息邮箱及其使用 消息队列及其使用学时:学时:6.06.0学时学时教学方法:讲授教学方法:讲授pptppt上机练上机练习点评案例分析习点评案例分析34.1 4.1 任务间的同步和事件控制块任务间的同步和事件控制块p 嵌入式系统中的各个任务都是以并发的方式运行,为同一个大任务服务。p 不可避免地要共同使用一些共享资源。p 多任务协作处理,需要相互的支持和限制。( 所以,系统必须具有完备的同步和通信机制。系统中的多个任务在运行时,

2、经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的限制和制约,才保证任务的顺利运行。因此,操作系统必须具有对任务的运行进行协调的能力,从而使任务之间可以无冲突、流畅地同步运行,而不致导致灾难性的后果。与人们依靠通信来互相沟通,从而使人际关系和谐、工作顺利的做法一样,计算机系统是依靠任务之间的良好通信来保证任务与任务的同步的。 44.1.1 任务间的同步O 为了实现各任务之间的合作和无冲突的运行,在各任务之间必须建立一些制约关系。 直接制约关系:源于任务之间的合作,也叫行为同步。 间接制约关系:源于对资源的共享,也叫资源同步。直接制约关系举例直接制约关系举

3、例:有2个任务,任务A和任务B,他们需要通过访问同一个数据缓冲区,合作完成一项工作,任务A负责向缓冲区写入数据,任务B负责从缓冲区读数据。显然,任务A还未向缓冲区写入数据时(缓冲区为空时),任务B因不能从缓冲区得到有效数据而处于等待状态。只有等任务A向缓冲区写入了数据后,才应该通知任务B去取数据。相反,当缓冲区的数据还未被任务B读取时(缓冲区还满时),任务A就不能向缓冲区写入新的数据而应该处于等待状态,只有等任务B自缓冲区读取数据后,才应该通知任务A去写入数据。间接制约关系举例间接制约关系举例:任务A和任务B共享一台打印机,如果系统已经把打印机分配给了任务A,则任务B因不能获得打印机的使用权而

4、应该处于等待状态,只有当任务A把打印机释放后,系统才能唤醒任务B使其获得打印机的使用权。5在多任务合作工作的过程中,由于任务间存在上述两种制约关系,操作系统应该解决两个问题: 各任务在访问同一共享资源时必须互斥。1. 相关的任务在执行上要配合协调,有先后次序。任务之间这种制约性的合作运行机制叫做任务间的同步。任务的同步是依靠任务之间互相发送消息来保证同步的。解决问题解决问题1-1-互斥关系互斥关系:即对于某个共享资源,如果一个任务正在使用,则其他任务只能等待,等到该任务释放该资源后,等待的任务之一才能使用它。解决问题解决问题2-2-先后次序先后次序:一个任务要等其伙伴发来通知,或建立了某个条件

5、后才能继续执行,否则只能等待。4.1.1 任务间的同步(续)总之,操作系统必须具有对任务的运行进行协调的能力,从而使任务之间可以无冲突、流畅的同步运行,而不致导致灾难性的后果。6多个任务共享同一资源或有工作顺序要求时,在正式工作之前要互相打招呼 。 黄宏:别走啊!宋丹丹:我自己的腿,我爱走就走,你管不着!黄宏:腿是你自己的,但手是咱俩的呀!74.1.2 事件事件任务1任务2发送事件请求事件图4-1 两个任务使用事件进行通信示意图O 任务间的同步依赖于任务间的通信。在uC/OS-II中,使用信号量、邮箱和消息队列这些被称作事件事件的中间环节来实现任务间的通信。任务1的责任是把信息发送到事件上,这

6、项操作叫做发送事件。任务2的责任是通过事件操作对事件进行查询:如果有信息,则读取信息,如果没有,则等待。读事件的操作叫做请求事件。uC/OS-II把任务发送事件、请求事件以及其他对事件的操作都定义为全局函数,以供应用程序的所有任务来调用。84.1.2.1 信号量6 使用信号量的目的:为共享资源设立一个表示该共享资源被占用情况的标志。I 日常生活中的共享资源-列车卫生间、公共停车场的使用规则。I 互斥型信号量、计数式信号量。信号量任务1任务2先请求信号量图4-2 两个任务使用信号量进行通信示意图后请求信号量1 - 0共享资源信号量任务1任务2发送信号量请求信号量0 - 1 - 0共享资源9例程

7、4-1:本例的应用程序中有2个用户任务:MyTask和YourTask。这两个任务都要访问同一个共享资源s,但YourTask访问s需要的时间长一些(本例中使用了一个循环来模拟访问的时间),而MyTask访问s的时间要短一些,这样就不可避免的出现了在任务YourTask访问s期间,任务MyTask也来访问s,从而出现了干扰。在例4-1的应用程序中定义一个全局变量ac_key来作为信号量,并根据该信号量的状态来访问共享资源s,以解决冲突问题。例程 4-2:如果把任务YourTask代码中的发信号语句ac_key=TRUE删掉,什么样结果?104.1.2.2 消息邮箱* 在多任务系统中,常常需要在

8、任务之间通过传递一个数据(这个数据叫做“消息”)的方式来进行通信。为达到这个目的,可在内存中创建一个存储空间作为该数据的缓冲区。如果把这个缓冲区叫做消息缓冲区,那么在任务间传递数据(消息)的一个最简单方法就是传递消息缓冲区的指针。. 用来传递消息缓冲区指针的数据结构就叫做消息邮箱。消息邮箱任务1任务2发送消息(发送消息缓冲区指针)图4-3 两个任务使用消息邮箱进行通信示意图请求消息(读取消息缓冲区指针)指针消息缓冲区11例程 4-3:下面是一个利用消息邮箱进行通信的例子:本例中有两个任务MyTask和YourTask。由于任务YourTask要向任务MyTask发送消息,因此定义了一个全局的指

9、针变量msg_p作为邮箱来传递消息的指针。124.1.2.3 消息队列7 消息邮箱不仅可以用来传递一个消息,而且也可定义一个指针数组。让数组的每个元素都存放一个消息缓冲区指针,那么任务就可通过传递这个指针数组指针的方法来传递多个消息。. 用来传递多个消息的数据结构就叫做消息队列。消息队列任务1任务2发送消息队列(发送消息缓冲区指针数组的指针)图4-4 两个任务使用消息对列进行通信示意图请求消息队列(读取消息缓冲区指针数组的指针)指针消息缓冲区1消息缓冲区n消息缓冲区指针数组134.1.2.4 等待任务列表(一个事件对多个任务)y 对等待任务需要具有管理功能,包括2个方面: 要对等待事件的所有任

10、务进行记录并排序; 应该允许任务有一定的等待时限。OSEventGrp1/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/0图4-5 事件的等待任务表任务等待表OSEventTbl在多任务系统中,当一个事件被占用时,其他请求该事件的任务在暂时得不到事件的服务时应该处于等

11、待状态。因此作为功能完善的事件,应该有对这些等任务有一定的管理功能。对于等待任务的记录,采用与任务就绪表类似的方法,数组OSEventTbl作为记录等待事件任务的记录表。在这个等待任务表中仍然是以任务的优先级别为顺序,令系统中的每个任务都在表中占据一位,并用该位为1表示这一位对应的任务为事件的等待任务。同样为了加快对该表的访问速度,定义变量OSEventGrp来表示等待任务表中的任务组。至于等待任务的等待时限,则记录在等待任务的任务控制块TCB的成员OSTCBDly中,并在每个时钟节拍中断服务程序中对该数据进行维护。每当有任务的等待时限已到时,将该任务从等待任务表中删除,并使它进入就绪态。14

12、4.1.3 事件控制块v 为了把描述事件的数据结构统一起来,uC/OS-II使用叫做事件控制块(ECB)的数据结构来描述诸如信号量、邮箱和消息队列这些事件。ECB中包含包括等待任务表在内的所有有关事件的数据。w 定义在文件uC/OS-II.H中的ECB的数据结构如下:typedef struct INT8U OSEventType; / 事件的类型 INT16U OSEventCnt; / 信号量计数器 void *OSEventPtr; / 消息或消息队列的指针 INT8U OSEventGrp; / 等待事件的任务组 INT8U OSEventTblOS_EVENT_TBL_SIZE; /

13、 任务等待列表 OS_EVENT;154.1.3 事件控制块(续)OSEventTypeOSEventCntOSEventPtrOSEventGrp1/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/0图4-6 事件控制块ECB的结构任务等待表OSEventTblOS_

14、EVENTpeventOSEventType的值说明OS_EVENT_TYPE_SEM表明事件是信号量OS_EVENT_TYPE_MUTEX表明事件是互斥性信号量OS_EVENT_TYPE_MBOX表明事件是消息邮箱OS_EVENT_TYPE_Q表明事件是消息队列OS_EVENT_TYPE_UNUSED空事件控制块(未被使用)表4-1 OSEventType可取的值与任务就绪表相似,结构成员OSEventGrp表示任务等待表中的各任务组是否存在等待任务。164.1.4 操作事件控制块的函数O uC/OS-II有4个对ECB进行基本操作的函数(定义在文件OS_CORE.C中),以供操作信号量、邮

15、箱、消息队列等事件的函数来调用。 事件控制块的初始化函数OS_EventWaitListInit():void OS_EventWaitListInit (OS_EVENT *pevent/ 事件控制块指针 ););P EventWaitListInit()的作用就是把变量OSEventGrp及任务等待表中的每一位清0,即令事件的任务等待表中不含有任何等待任务。此函数将在某任务调用函数OSXXXCreat()创建事件时,被函数OSXXXCreat()所调用。XXX含义Sem对信号量进行操作的函数Mutex对互斥信号量进行操作的函数Mbox对消息邮箱进行操作的函数Q对消息队列进行操作的函数174

16、.1.4 操作事件控制块的函数(续) 使一个任务进入等待状态的函数OSOS_EventTaskWait():void OS_EventTaskWait (OS_EVENT *pevent/ 事件控制块指针 );P函数OS_EventTaskWait()OS_EventTaskWait()将在任务调用函数OSXXXPend()请求一个事件时,被函数OSXXXPend()所调用。 使一个正在等待任务进入就绪状态的函数OS_OS_EventTaskRdy():void OS_EventTaskRdy (OS_EVENT *pevent,/ 事件控制块指针void *msg,/ 未使用INT8U ms

17、k/ 清除TCB状态标志掩码 );P该函数作用是当某个事件发生了,要将等待该事件的任务列表中最高优先级的任务置于就绪态(把该任务在等待表中对应的位清0,再把该任务在就绪表中对应的位置1)然后引发一次任务调度。函数OS_EventTaskRdy()OS_EventTaskRdy()将在某任务调用函数OSXXXPost()发送一个事件时,被函数OSXXXPost()所调用。184.1.4 操作事件控制块的函数(续) 使一个等待超时的任务进入就绪状态的函数OS_OS_EventTo():void OS_EventTO(OS_EVENT *pevent/ 事件控制块指针 );P如果一个正在等待事件的任

18、务已经超过了等待时间,却仍因为没有获取事件等原因而未具备可以运行的条件,却又要使它进入就绪态,这时要调用此函数。函数OS_EventTo()OS_EventTo()将在某一任务调用函数OSXXXPend()请求一个事件时,被函数OSXXXPend()所调用。194.1.5 空事件控制块链表OSEventTypeOSEventCntOSEventPtrOSEventGrp765432101514131211109823222120191817163130292827262524393837363534333247464544434241405554535251504948636261605958

19、5756OSEventFreeListOSEventTypeOSEventCntOSEventPtrOSEventGrp7654321015141312111098232221201918171631302928272625243938373635343332474645444342414055545352515049486362616059585756OSEventTypeOSEventCntOSEventPtrOSEventGrp765432101514131211109823222120191817163130292827262524393837363534333247464544434

20、24140555453525150494863626160595857560共SO_MAX_EVENTS个事件控制块图4-7 空事件控制块链表P 初始化时,系统会在初始化函数OSInit()中按应用程序使用事件的总数OS_MAX_EVENTS,创建OS_MAX_EVENTS个空事件控制块并借用成员OSEventPtr作为链接指针,把这些空事件控制块链接单向链表。由于链表中的所有控制块尚未与具体事件相关联,因此该链表叫做空事件控制块链表。以后,每当应用程序创建一个事件时,系统就会从链表中取出一个空事件控制块,并对它进行初始化以描述该事件。而当应用程序删除一个事件时,就会将该事件的控制块归还给空事

21、件控制块链表。204.2 4.2 信号量及其操作信号量及其操作p 当ECB成员OSEventType设置为OS_EVENT_SEM时,这个ECB描述的就是一个信号量。信号量由信号量计数器和任务等待表两部分组成。p 有任务申请信号量,若EventCnt0,则OSEventCnt减1;若OSEventCnt=0,将任务列入OSEventTbl,而使任务处于等待状态。p 有任务释放信号量,则在OSEventTbl表中找出优先级最高的等待任务,并在使它就绪后引发一次调度。p 若任务等待表中没有等待任务,则信号量计数器就只简单地加1。OS_EVENT_TYPE_SEM3NULL0 x0710010000

22、00000100000010000000000000000000000000000000000000000000图4-8 一个信号量的事件控制块任务等待表OSEventTblpevent使用事件控制块成员OSEventCnt作为计数器有4个等待任务的信号量信号量不使用事件控制块成员OSEventPtr(消息(队列)指针)214.2.1 信号量的操作OS_EVENT *OSSemCreate (INT16U cnt) OS_EVENT *pevent; OS_ENTER_CRITICAL(); pevent = OSEventFreeList;pevent = OSEventFreeList;

23、/ / 从空余事件控制块链表中获得一个事件控制块从空余事件控制块链表中获得一个事件控制块 if (OSEventFreeList != (OS_EVENT *)0) OSEventFreeList = (OS_EVENT OSEventFreeList = (OS_EVENT * *)OSEventFreeList-OSEventPtr)OSEventFreeList-OSEventPtr; / / 使空余控制块链表指针指向下一个空余的事件控制块使空余控制块链表指针指向下一个空余的事件控制块 OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) pe

24、vent - OSEventType = OS_EVENT_TYPE_SEM; pevent - OSEventType = OS_EVENT_TYPE_SEM; / / 设置为信号量设置为信号量 pevent - OSEventCnt = cnt; / pevent - OSEventCnt = cnt; / 置计数器初值置计数器初值 pevent - OSEventPtr = (void pevent - OSEventPtr = (void * *)0; / )0; / 置空指针置空指针 OSEventWaitListInit(pevent); / OSEventWaitListInit

25、(pevent); / 初始化控制块初始化控制块 return (pevent);思考:一个刚创建且计数器初值为10的信号量示意图是怎样的? 创建信号量OSSemCreate():OS_EVENT *OSSemCreate (/ 返回值为已创建的信号量的指针INT16U cnt/ 信号量计数器初值 );224.2.1 信号量的操作(续) 请求信号量OSSemPend():void OSSemPend (OS_EVENT *pevent;/ 信号量的指针INT16U timeout;/ 等待时限INT8U err/ 错误信息 );INT16U OSSemAccept ( OS_EVENT *pe

26、vent;/ 信号量的指针 );当一个任务请求信号量时,若希望在信号量无效时准许任务不进入等待状态而继续运行,则不调用OSSemPend(),而是调用OSSemAccept()来请求信号量。 若参数timeout被设置为0,则表明任务的等待时间为无限长。当任务需要访问一个共享资源时,若信号量无效(即OSEventCnt=0)则会在等待任务表中把该任务对应的位置1而让任务处于等待状态,并把等待时限timeout保存在任务控制块TCB的成员OSTCBDly中。234.2.1 信号量的操作(续) 发送信号量OSSemPost():INT8U OSSemPost (OS_EVENT *pevent;/

27、 信号量的指针 );p 任务获得信号量,并在访问共享资源结束后,必须释放信号量!释放信号量也叫发送信号量。函数OSSemPost()在对信号量的计数器操作之前,首先要检查是否还有等待该信号量的任务,如果没有,就把信号量计数器OSEventCnt加1,如果有,则调用调度器OS_Sched()去运行等待任务中优先级最高的任务。 24例程4-4:试编写一个应用程序,其中有一个函数Fun()和两个任务MyTask和YourTask。应用程序中的两个任务都可以调用函数Fun(),但不能同时调用。 练习4-1:应用程序中有一个函数Fun(),如果想使任务MyTask()必须经过任务YourTask()允许

28、(YourTask()不申请资源,只负责释放,例如YourTask()每执行第五次时释放)才能调用这个函数一次,试写出这两个任务的示意性代码。 从代码中可以看到,使用事件控制块描述的信号量,无论在使用方面还是在信号量的完善性方面,他的确比简单形式的信号量要方便得多。254.2.1 信号量的操作(续) 删除信号量OSSemDel():OS_EVENT *OSSemDel ( OS_EVENT *pevent;/ 信号量的指针 INT8U opt;/ 删除条件选项 INT8U *err/ 错误信息 );p opt有两个参数选择: 选择OS_DEL_NO_PEND,表示当等待任务表中已没有等待任务时

29、才删除信号量。 选择OS_DEL_ALLWAYS,表示在等待任务表中无论是否有等待任务都立即删除信号量。M 注意:只能在任务中删除信号量,不能在中断服务程序中删除!264.2.1 信号量的操作(续) 查询信号量的状态OSSemQuery():INT8U OSSemQuery ( OS_EVENT *pevent;/ 信号量的指针 OS_SEM_DATA *pdata/ 存储信号量状态的结构 );p 参数pdata是一个OS_SEM_DATA结构的指针, OS_SEM_DATA结构如下: typedef structINT16U OSCnt;INT8U OSEventTblOS_EVENT_TB

30、L_SIZE;INT8U OSEventGrp; OS_SEM_DATA;w 调用此函数后,会把信号量中相关信息存储到OS_SEM_DATA 类型的变量中。因此在调用OSSemQuery()之前,需先定义一个OS_SEM_DATA结构类型的变量。274.3 4.3 互斥信号量和优先级反转互斥信号量和优先级反转图4-9 任务优先级反转示意图任务任务A(A(高高) )任务任务B(B(中中) )任务任务C(C(低低) )t1t2t3t4t5t6任务A等待的事件来临任务A申请信号量任务A获得信号量任务A因优先级高于任务C而运行任务A因任务C未释放信号量而等待任务A因任务C释放了信号量而运行任务B等待的

31、事件来临任务B因优先级高于任务C而运行任务C获得信号量任务C使用共享资源任务C因任务A获得CPU而等待任务C因任务A等待信号量而继续运行任务C因任务B获得CPU而等待任务C因任务B释放了CPU而运行任务C释放信号量28之所以出现上述的优先级反转现象,是因为一个优先级别较低的任务在获得了信号量使用共享资源期间,被具有较高优先级别的任务所打断而不释放信号量,从而使正在等待这个信号量的更高级别的任务因得不到信号量而被迫处于等待状态,在这个等待期间,就让优先级别低于它而高于占据信号量的任务先运行了。显然,如果这种优先级别介于使用信号量的两个任务优先级别之间的中等优先级别任务较多,则会极大的恶化高优先级

32、别任务的运行环境,是实时系统所无法容忍的。29解决问题的办法之一:提升任务C的优先级,资源使用后再予以恢复。具体做法是使获得信号量任务的优先级别在使用共享资源期间高于允许使用该资源的任何任务,以使该任务不被不使用共享资源的中等优先级的任务所打断,从而能尽快地使用完共享资源并释放信号量。然后在释放了信号量之后再恢复该任务原来的优先级别。 30例程 4-5:下面是一个使用信号量实现独占式访问共享资源而出现了任务优先级反转的应用程序实例。请运行该程序并分析它的运行结果。 HerTask获得信号量而运行MyTask得不到信号量而不能运行YouTask运行MyTask获得信号量而运行图4-10 优先级反

33、转现象使用信号量的任务是否能够运行是受任务的优先级以及是否占用信号量两个条件约束的,而信号量的约束高于优先级的约束。314.3.1 互斥型信号量p 任务可以用互斥型信号量来实现对共享资源的独占式处理。p 解决优先级反转问题。q 成员OSEventCnt被分成了低8位和高8位两部分:低8位用来存放信号值(该值为0 xFF时,信号有效),高8位用来存放为了避免出现优先级反转而要提升的优先级prio。OS_EVENT_TYPE_MUTEXPrio | 0 xFFNULL0 x000000000000000000000000000000000000000000000000000000000000000

34、000图4-11 互斥型信号量结构任务等待表OSEventTblpevent324.3.2 互斥型信号量的操作 创建互斥型信号量OSMutexCreate():OS_EVENT *OSMutexCreate (INT8U prio,/ 优先级别INT8U *err/ 错误信息 ); 请求互斥型信号量OSMutexPend()、 OSMutexAccept():void OSMutexPend ( OS_EVENT *pevent, / 互斥型信号量指针 INT16U timeout,/ 等待时限INT8U *err/ 错误信息 );void OSMutexAccept ( OS_EVENT *

35、pevent, / 互斥型信号量指针 INT8U *err/ 错误信息 );无等待的请求一个互斥型信号量函数334.3.2 互斥型信号量的操作(续) 发送互斥型信号量OSMutexPost():INT8U OSMutexPost ( OS_EVENT *pevent / 互斥型信号量指针 ); 获取互斥型信号量的当前状态OSMutexQuery():INT8U OSMutexQuery ( OS_EVENT *pevent, / 互斥型信号量指针 OS_MUTEX_DATA *pdata / 存放互斥型信号量状态的结构 ); 删除互斥型信号量OSMutexDel():OS_EVENT OSMu

36、texDel ( OS_EVENT *pevent, / 互斥型信号量指针 INT8U opt, / 删除方式选项 INT8U *err / 错误信息 );34练习 4-6:在例4-5的应用程序中,把使用的信号量改为互斥型信号量,然后运行该程序并观察其运行结果。 图4-12 使用互斥型信号量消除了优先级反转现象HerTask获得信号量而运行MyTask请求信号量MyTask获得信号量而运行YouTask运行354.4 4.4 消息邮箱及其操作消息邮箱及其操作OS_EVENT_TYPE_MBOX0 x0000&message0 x00000000000000000000000000000

37、0000000000000000000000000000000000000图4-13 消息邮箱的结构任务等待表OSEventTblpevent消息数据缓冲区OSEventTypeOSEventCntOSEventPtrOSEventGrpp 任务间需要传递数据,在内存中建立缓冲区。以该缓冲区为中介来实现任务 间的数据传递。p 数据缓冲区指针赋给OSEventPtr,且使OSEventType为OS_EVENT_TYPE_MBOX。q 消息邮箱是在两个需要通信的任务之间通过传递数据缓冲区指针的方法来通信的。364.4.1 消息邮箱的操作 创建消息邮箱OSMboxCreate():OS_EVENT

38、 OSMboxCreate ( void *msg / 消息缓冲区指针 );OS_EVENT *OSMboxCreate (void *msg) OS_EVENT *pevent; OS_ENTER_CRITICAL(); pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) OSEventFreeList = (OS_EVENT *)OSEventFreeList-OSEventPtr; OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) pevent-OSEventTyp

39、e = OS_EVENT_TYPE_MBOX; pevent-OSEventType = OS_EVENT_TYPE_MBOX; pevent - OSEventCnt = 0; pevent - OSEventCnt = 0; pevent-OSEventPtr = msg; pevent-OSEventPtr = msg; OSEventWaitListInit(pevent); OSEventWaitListInit(pevent); return (pevent); 函数中的参数msg为消息的指针,函数的返回值为消息邮箱的指针。374.4.1 消息邮箱的操作(续) 向消息邮箱发送消息之

40、OSMboxPost():INT8U OSMboxPost ( OS_EVENT *pevent,/ 消息邮箱指针 void *msg / 消息缓冲区指针 ); 向消息邮箱发送消息之OSMboxPostOpt():INT8U OSMboxPostOpt ( OS_EVENT *pevent,/ 消息邮箱指针 void *msg, / 消息缓冲区指针 INT8U opt/ 广播选项 );r 这个函数可以以广播的形式向事件等待任务表中的所有任务发送消息。参数opt用来说明是否把消息向所有等待的任务广播。若为OS_POST_OPT_BROADCAST,则意味着把消息向所有等待任务广播,将所有等待任务

41、从等待列表中删除。若该值为OS_POST_OPT_NONE,则把消息只向优先级别最高的等待任务发送,那么只有最高优先级的任务进入就绪态,准备运行。384.4.1 消息邮箱的操作(续) 请求消息邮箱之OSMboxPend():void *OSMboxPend ( OS_EVENT *pevent,/ 消息邮箱指针 INT16U timeout,/ 等待时限 INT8U *err / 错误信息 );请求消息邮箱之OSMboxAccept():void *OSMboxAccept ( OS_EVENT *pevent,/ 消息邮箱指针 );该函数与OSMboxPend()的区别在于获取消息失败时,任

42、务不进行等待而继续运行。这个函数的主要作用查看邮箱指针OSEventPtr是否为NULL,如果不为NULL,则把邮箱中的消息指针返回给调用函数的任务。如果获取消息失败则使任务进入等待状态,并引发一次任务调度。394.4.1 消息邮箱的操作(续) 查询邮箱的状态OSMboxQuery():INT8U OSMboxQuery ( OS_EVENT *pevent,/ 消息邮箱指针 OS_MBOX_DATA *pdata/ 存放邮箱信息的数据结 构指针 ); 删除邮箱OSMboxDel():OS_EVENT *OSMboxDel ( OS_EVENT *pevent,/ 消息邮箱指针 INT8U o

43、pt,/ 删除选项 INT8U *err/ 错误信息 );40例程 4-7:设计一个应用程序,该程序有两个任务MyTask和YourTask。在任务MyTask中用一个变量Times记录任务MyTask的运行次数,并将其作为消息用邮箱Str_Box发给任务YourTask,且由任务YourTask显示出来。414.5 4.5 消息队列及其操作消息队列及其操作p 使用消息队列可在任务之间传递多条消息。p 消息队列由3部分组成:事件控制块、 队列控制块和消息。p 使OSEventType为OS_EVENT_TYPE_Q。OS_EVENT_TYPE_Q0 x00000 x00peventOS_EVE

44、NTOSEventTypeOSEventCntOSEventPtrOSEventGrpOSEventTbl. OSQPtr. OSQStart. OSQSize. OSQOut. OSQin. OSQEnd. OSQEntriesvoid *MsgTblmessagemessagemessagemessagemessagemessageOSQEntriesOSQSize图4-14 消息队列的数据结构(事件控制块、队列控制块、消息指针数组和消息之间的关系)OS_Q消息队列相当于一个共用一个任务等待列表的消息邮箱数组,事件控制块成员OSEventPtr指向一个叫做队列控制块(OS_Q)的结构,该结

45、构管理一个数组MSGTbl,该数组中的元素都是一些指向消息的指针。424.5.1 消息指针数组图4-15 消息指针数组是一个环形的数据缓冲区参数说明OSQSize数组的长度(可容纳总的消息数,最大值65,535)OSQEntries已存放消息指针的元素数目OSQStart常指针,指向消息指针数组的起始地址OSQEnd常指针,指向消息指针数组结束单元的下一个单元。它使得数组构成了一个循环的缓冲区。OSQin指针,指向插入一条消息的位置。(空)当它移动到与OSQEnd相等时,被调整到指向数组的起始单元。OSQOut指针,指向被取出消息的位置。(满)当它移动到与OSQEnd相等时,被调整到指向数组的

46、起始单元。表5-2 图5-14中各参数含义p 向指针数组中插入消息指针的方式有2种:先进先出(FIFO)方式和后进先出(LIFO)方式。当采用先进先出方式时,消息队列将在指针OSQIn指向的位置插入消息指针,而把指针OSQOut指向的位置作为输出。(插入消息时从低地址往高地址生长)当采用后进先出方式时,则只使用指针OSQOut,当向队列插入消息指针时,指针OSQOut将先移动到图4-15虚线所示位置,再按指针OSQOut指向的位置插入消息指针,输出时指针OSQOut无须进行移动,就把指针OSQOut指向的消息指针输出。(插入消息时从高地址往低地址生长)434.5.2 队列控制块r 为了对消息指

47、针数组进行管理,把消息指针数组的基本参数都记录在一个叫做队列控制块的结构中。typedef struct os_q struct os_q * OSQPtr; void * OSQStart; void * OSQEnd; void * OSQIn; void * OSQOut; INT16U OSQSize; INT16U OSQEntries; OS_Q;OSQPtrOSQStartOSQSizeOSQOutOSQinOSQEndOSQEntriesOS_QOSQPtrOSQStartOSQSizeOSQOutOSQinOSQEndOSQEntriesOS_QOSQPtrOSQStartO

48、SQSizeOSQOutOSQinOSQEndOSQEntriesOS_QOSQPtrOSQStartOSQSizeOSQOutOSQinOSQEndOSQEntriesOS_QOSQFreeList0共OS_MAX_QS个空队列控制块图4-16 空队列控制块链表每当任务创建一个消息队列时,就会在空队列控制块链表中摘取一个控制块供消息队列来使用,并令该消息队列事件控制块中的指针OSEventPtr指向这个队列控制块;而当任务释放一个消息队列时,就会将该消息队列使用的队列控制块归还空队列控制块链表。初始化时,系统将按文件OS_CFG.H中的配置常数OS_MAX_QS定义OS_MAX_QS个队列控

49、制块,并用队列控制块中的指针OSQPtr将所有队列控制块链接为链表。由于这时还没有使用他们,因此这个链表叫做空队列控制块链表。444.5.3 消息队列的操作 创建消息队列OSQCreate():OS_EVENT OSQCreate ( void *start / 指针数组的地址 INT16U size / 数组长度 );p 创建一个消息队列首先需要定义一个指针数组,然后把各个消息数据缓冲区的首地址存入该数组中,最后再调用OSQCreate()创建消息队列(需要一个事件控制块结点和一个队列控制块结点)。p 参数start为存放消息缓冲区指针数组的地址,参数size为该数组的大小,函数的返回值为消息队列的指针。p 函数OSQCrea

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论