ImageVerifierCode 换一换
格式:DOCX , 页数:26 ,大小:101.45KB ,
资源ID:10091458      下载积分:12 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/10091458.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(第11章 消息队列管理.docx)为本站会员(b****7)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

第11章 消息队列管理.docx

1、第11章 消息队列管理第11章 消息队列管理消息队列是C/OS-II中另一种通讯机制,它可以使一个任务或者中断服务子程序向另一个任务发送以指针方式定义的变量。因具体的应用有所不同,每个指针指向的数据结构变量也有所不同。为了使用C/OS-II的消息队列功能,需要在OS_CFG.H 文件中,将OS_Q_EN常数设置为1,并且通过常数OS_MAX_QS来决定C/OS-II支持的最多消息队列数。在使用一个消息队列之前,必须先建立该消息队列。这可以通过调用OSQCreate()函数(见11.07.01节),并定义消息队列中的单元数(消息数)来完成。C/OS-II提供了9个对消息队列进行操作的函数:OSQ

2、Create(),OSQDel(),OSQPend(),OSQPost(),OSQPostFront(),OSQPostOpt(),OSQAccept(),OSQFlush()和OSQQuery()函数。图 F11.7是任务、中断服务子程序和消息队列之间的关系。其中,消息队列的符号很像多个邮箱。实际上,我们可以将消息队列看作时多个邮箱组成的数组,只是它们共用一个等待任务列表。每个指针所指向的数据结构是由具体的应用程序决定的。N代表了消息队列中的总单元数。当调用OSQPend()或者OSQAccept()之前,调用N次OSQPost()或者OSQPostFront()就会把消息队列填满。从图 F

3、11.7中可以看出,一个任务或者中断服务子程序可以调用OSQPost(),OSQPostFront(),OSQFlush()或者OSQAccept()函数。但是,只有任务可以调用OSQPend()和OSQQuery()函数。图 F11.7 任务、中断服务子程序和消息队列之间的关系Figure 11.7图 F11.8是实现消息队列所需要的各种数据结构。这里也需要事件控制块来记录等待任务列表F11.8(1),而且,事件控制块可以使多个消息队列的操作和信号量操作、邮箱操作相同的代码。当建立了一个消息队列时,一个队列控制块(OS_Q结构,见OS_Q.C文件)也同时被建立,并通过OS_EVENT中的.O

4、SEventPtr域链接到对应的事件控制块F11.8(2)。在建立一个消息队列之前,必须先定义一个含有与消息队列最大消息数相同个数的指针数组F11.8(3)。数组的起始地址以及数组中的元素数作为参数传递给OSQCreate()函数。事实上,如果内存占用了连续的地址空间,也没有必要非得使用指针数组结构。文件OS_CFG.H中的常数OS_MAX_QS定义了在C/OS-II中可以使用的最大消息队列数,这个值最小应为2。C/OS-II在初始化时建立一个空闲的队列控制块链表,如图 F11.9所示。图F11.8 用于消息队列的数据结构Figure 11.8图F11.9 空闲队列控制块链表Figure 11

5、.9队列控制块是一个用于维护消息队列信息的数据结构,它包含了以下的一些域。这里,仍然在各个变量前加入一个.来表示它们是数据结构中的一个域。.OSQPtr在空闲队列控制块中链接所有的队列控制块。一旦建立了消息队列,该域就不再有用了。.OSQStart是指向消息队列的指针数组的起始地址的指针。用户应用程序在使用消息队列之前必须先定义该数组。.OSQEnd是指向消息队列结束单元的下一个地址的指针。该指针使得消息队列构成一个循环的缓冲区。.OSQIn是指向消息队列中插入下一条消息的位置的指针。当.OSQIn和.OSQEnd相等时,.OSQIn被调整指向消息队列的起始单元。.OSQOut是指向消息队列中

6、下一个取出消息的位置的指针。当.OSQOut和.OSQEnd相等时,.OSQOut被调整指向消息队列的起始单元。.OSQSize是消息队列中总的单元数。该值是在建立消息队列时由用户应用程序决定的。在C/OS-II中,该值最大可以是65,535。.OSQEntries是消息队列中当前的消息数量。当消息队列是空的时,该值为0。当消息队列满了以后,该值和.OSQSize值一样。 在消息队列刚刚建立时,该值为0。消息队列最根本的部分是一个循环缓冲区,如图F11.10。其中的每个单元包含一个指针。队列未满时,.OSQIn F11.10(1)指向下一个存放消息的地址单元。如果队列已满(.OSQEntrie

7、s与.OSQSize相等),.OSQIn F11.10(3)则与.OSQOut指向同一单元。如果在.OSQIn指向的单元插入新的指向消息的指针,就构成FIFO(First-In-First-Out)队列。相反,如果在.OSQOut指向的单元的下一个单元插入新的指针,就构成LIFO队列(Last-In-First-Out)F11.10(2)。当.OSQEntries和.OSQSize相等时,说明队列已满。消息指针总是从.OSQOut F11.10(4)指向的单元取出。指针.OSQStart和.OSQEnd F11.10(5)定义了消息指针数组的头尾,以便在.OSQIn和.OSQOut到达队列的边

8、缘时,进行边界检查和必要的指针调整,实现循环功能。图F11.10 消息队列是一个由指针组成的循环缓冲区Figure 11.1011.00建立一个消息队列,OSQCreate()程序清单 L11.21是OSQCreate()函数的源代码。该函数需要一个指针数组来容纳指向各个消息的指针。该指针数组必须声名为void类型。OSQCreate()首先从空闲事件控制块链表中取得一个事件控制块(见图F11.3)L11.21(1),并对剩下的空闲事件控制块列表的指针做相应的调整,使它指向下一个空闲事件控制块L11.21(2)。接着,OSQCreate()函数从空闲队列控制块列表中取出一个队列控制块L11.2

9、1(3)。如果有空闲队列控制块是可以的,就对其进行初始化L11.21(4)。然后该函数将事件控制块的类型设置为OS_EVENT_TYPE_Q L11.21(5),并使其.OSEventPtr指针指向队列控制块L11.21(6)。OSQCreate()还要调用OSEventWaitListInit()函数对事件控制块的等待任务列表初始化见11.01节,初始化一个事件控制块,OSEventWaitListInit() L11.21(7)。因为此时消息队列正在初始化,显然它的等待任务列表是空的。最后,OSQCreate()向它的调用函数返回一个指向事件控制块的指针L11.21(9)。该指针将在调用O

10、SQPend(),OSQPost(),OSQPostFront(),OSQFlush(),OSQAccept()和OSQQuery()等消息队列处理函数时使用。因此,该指针可以被看作是对应消息队列的句柄。值得注意的是,如果此时没有空闲的事件控制块,OSQCreate()函数将返回一个NULL指针。如果没有队列控制块可以使用,为了不浪费事件控制块资源,OSQCreate()函数将把刚刚取得的事件控制块重新返还给空闲事件控制块列表 L11.21(8)。另外,消息队列一旦建立就不能再删除了。试想,如果有任务正在等待某个消息队列中的消息,而此时又删除该消息队列,将是很危险的。程序清单 L11.21 建

11、立一个消息队列OS_EVENT *OSQCreate (void *start, INT16U size)#if OS_CRITICAL_METHOD = 3 OS_CPU_SR cpu_sr;#endif OS_EVENT *pevent; OS_Q *pq;if (OSIntNesting 0) return (OS_EVENT *)0); OS_ENTER_CRITICAL(); pevent = OSEventFreeList; (1) if (OSEventFreeList != (OS_EVENT *)0) OSEventFreeList = (OS_EVENT *)OSEvent

12、FreeList-OSEventPtr; (2) OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) OS_ENTER_CRITICAL(); pq = OSQFreeList; (3) if (OSQFreeList != (OS_Q *)0) OSQFreeList = OSQFreeList-OSQPtr; OS_EXIT_CRITICAL(); if (pq != (OS_Q *)0) pq-OSQStart = start; (4) pq-OSQEnd = &startsize; pq-OSQIn = start; pq-OSQOut =

13、 start; pq-OSQSize = size; pq-OSQEntries = 0; pevent-OSEventType = OS_EVENT_TYPE_Q; (5) pevent-OSEventPtr = pq; (6) OSEventWaitListInit(pevent); (7) else OS_ENTER_CRITICAL(); pevent-OSEventPtr = (void *)OSEventFreeList; (8) OSEventFreeList = pevent; OS_EXIT_CRITICAL(); pevent = (OS_EVENT *)0; return

14、 (pevent); (9)11.01删除一个消息队列,OSQDel()#if OS_Q_DEL_EN 0OS_EVENT *OSQDel (OS_EVENT *pevent, INT8U opt, INT8U *err)#if OS_CRITICAL_METHOD = 3 OS_CPU_SR cpu_sr;#endif BOOLEAN tasks_waiting; OS_Q *pq; if (OSIntNesting 0) *err = OS_ERR_DEL_ISR; return (OS_EVENT *)0); #if OS_ARG_CHK_EN 0 if (pevent = (OS_EV

15、ENT *)0) *err = OS_ERR_PEVENT_NULL; return (pevent); if (pevent-OSEventType != OS_EVENT_TYPE_Q) *err = OS_ERR_EVENT_TYPE; return (pevent); #endif OS_ENTER_CRITICAL(); if (pevent-OSEventGrp != 0x00) tasks_waiting = TRUE; else tasks_waiting = FALSE; switch (opt) case OS_DEL_NO_PEND: if (tasks_waiting

16、= FALSE) pq = (OS_Q *)pevent-OSEventPtr; pq-OSQPtr = OSQFreeList; OSQFreeList = pq; pevent-OSEventType = OS_EVENT_TYPE_UNUSED; pevent-OSEventPtr = OSEventFreeList; OSEventFreeList = pevent; OS_EXIT_CRITICAL(); *err = OS_NO_ERR; return (OS_EVENT *)0); else OS_EXIT_CRITICAL(); *err = OS_ERR_TASK_WAITI

17、NG; return (pevent); case OS_DEL_ALWAYS: while (pevent-OSEventGrp != 0x00) OS_EventTaskRdy(pevent, (void *)0, OS_STAT_Q); pq = (OS_Q *)pevent-OSEventPtr; pq-OSQPtr = OSQFreeList; OSQFreeList = pq; pevent-OSEventType = OS_EVENT_TYPE_UNUSED; pevent-OSEventPtr = OSEventFreeList; OSEventFreeList = peven

18、t; OS_EXIT_CRITICAL(); if (tasks_waiting = TRUE) OS_Sched(); *err = OS_NO_ERR; return (OS_EVENT *)0); default: OS_EXIT_CRITICAL(); *err = OS_ERR_INVALID_OPT; return (pevent); #endif11.02等待一个消息队列中的消息,OSQPend()程序清单 L11.22是OSQPend()函数的源代码。OSQPend()函数首先检查事件控制块是否是由OSQCreate()函数建立的L11.22(1),接着,该函数检查消息队列中是

19、否有消息可用(即.OSQEntries是否大于0)L11.22(2)。如果有,OSQPend()函数将指向消息的指针复制到msg变量中,并让.OSQOut指针指向队列中的下一个单元L11.22(3),然后将队列中的有效消息数减1 L11.22(4)。因为消息队列是一个循环的缓冲区,OSQPend()函数需要检查.OSQOut是否超过了队列中的最后一个单元 L11.22(5)。当发生这种越界时,就要将.OSQOut重新调整到指向队列的起始单元 L11.22(6)。这是我们调用OSQPend()函数时所期望的,也是执行OSQPend()函数最快的路径。程序清单 L11.22 在一个消息队列中等待一

20、条消息void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)#if OS_CRITICAL_METHOD = 3 OS_CPU_SR cpu_sr;#endif void *msg; OS_Q *pq;if (OSIntNesting 0) *err = OS_ERR_PEND_ISR; return (void *)0); #if OS_ARG_CHK_EN 0 if (pevent = (OS_EVENT *)0) *err = OS_ERR_PEVENT_NULL; return (void *)0); if (peven

21、t-OSEventType != OS_EVENT_TYPE_Q) *err = OS_ERR_EVENT_TYPE; return (void *)0); #endif OS_ENTER_CRITICAL(); pq = pevent-OSEventPtr; if (pq-OSQEntries != 0) (2) msg = *pq-OSQOut+; (3) pq-OSQEntries-; (4) if (pq-OSQOut = pq-OSQEnd) (5) pq-OSQOut = pq-OSQStart; (6) OS_EXIT_CRITICAL(); *err = OS_NO_ERR;

22、return(msg); OSTCBCur-OSTCBStat |= OS_STAT_Q; (8) OSTCBCur-OSTCBDly = timeout; OSEventTaskWait(pevent); OS_EXIT_CRITICAL(); OSSched(); (9) OS_ENTER_CRITICAL(); if (msg = OSTCBCur-OSTCBMsg) != (void *)0) (10) OSTCBCur-OSTCBMsg = (void *)0; OSTCBCur-OSTCBStat = OS_STAT_RDY; OSTCBCur-OSTCBEventPtr = (O

23、S_EVENT *)0; (11) OS_EXIT_CRITICAL(); *err = OS_NO_ERR;return(msg); OSEventTO(pevent); (13) OS_EXIT_CRITICAL(); msg = (void *)0; (14) *err = OS_TIMEOUT; return (msg); (17)如果这时消息队列中没有消息(.OSEventEntries是0),OSQPend()函数检查它的调用者是否是中断服务子程序L11.22(7)。象OSSemPend()和OSMboxPend()函数一样,不能在中断服务子程序中调用OSQPend(),因为中断服

24、务子程序是不能等待的。但是,如果消息队列中有消息,即使从中断服务子程序中调用OSQPend()函数,也一样是成功的。如果消息队列中没有消息,调用OSQPend()函数的任务被挂起L11.22(8)。当有其它的任务向该消息队列发送了消息或者等待时间超时,并且该任务成为最高优先级任务时,OSSched()返回L11.22(9)。这时,OSQPend()要检查是否有消息被放到该任务的任务控制块中L11.22(10)。如果有,那么该次函数调用成功,把任务的任务控制块中指向消息队列的指针删除L11.22(17),并将对应的消息被返回到调用函数L11.22(17)。在OSQPend()函数中,通过检查任务

25、的任务控制块中的.OSTCBStat域,可以知道是否等到时间超时。如果其对应的OS_STAT_Q位被置1,说明任务等待已经超时L11.22(12)。这时,通过调用函数OSEventTo()可以将任务从消息队列的等待任务列表中删除L11.22(13)。这时,因为消息队列中没有消息,所以返回的指针是NULLL11.22(14)。如果任务控制块标志位中的OS_STAT_Q位没有被置1,说明有任务发出了一条消息。OSQPend()函数从队列中取出该消息L11.22(15)。然后,将任务的任务控制中指向事件控制块的指针删除L11.22(16)。11.03向消息队列发送一个消息(FIFO),OSQPost

26、()程序清单 L11.23是OSQPost()函数的源代码。在确认事件控制块是消息队列后 L11.23(1),OSQPost()函数检查是否有任务在等待该消息队列中的消息L11.23(2)。当事件控制块的.OSEventGrp域为非0值时,说明该消息队列的等待任务列表中有任务。这时,调用OSEventTaskRdy()函数 见11.02节,使一个任务进入就绪状态,OSEventTaskRdy()从列表中取出最高优先级的任务L11.23(3),并将它置于就绪状态。然后调用函数OSSched() L11.23(4)进行任务的调度。如果上面取出的任务的优先级在整个系统就绪的任务里也是最高的,而且OS

27、QPost()函数不是中断服务子程序调用的,就执行任务切换,该最高优先级任务被执行。否则的话,OSSched()函数直接返回,调用OSQPost()函数的任务继续执行。程序清单 L11.23 向消息队列发送一条消息INT8U OSQPost (OS_EVENT *pevent, void *msg)#if OS_CRITICAL_METHOD = 3 OS_CPU_SR cpu_sr;#endif OS_Q *pq;#if OS_ARG_CHK_EN 0 if (pevent = (OS_EVENT *)0) return (OS_ERR_PEVENT_NULL); if (msg = (vo

28、id *)0) return (OS_ERR_POST_NULL_PTR); if (pevent-OSEventType != OS_EVENT_TYPE_Q) return (OS_ERR_EVENT_TYPE); #endif OS_ENTER_CRITICAL(); if (pevent-OSEventGrp) (2) OSEventTaskRdy(pevent, msg, OS_STAT_Q); (3) OS_EXIT_CRITICAL(); OSSched(); (4) return (OS_NO_ERR); else pq = pevent-OSEventPtr; if (pq-

29、OSQEntries = pq-OSQSize) (5) OS_EXIT_CRITICAL(); return (OS_Q_FULL); else *pq-OSQIn+ = msg; (6) pq-OSQEntries+; if (pq-OSQIn = pq-OSQEnd) pq-OSQIn = pq-OSQStart; OS_EXIT_CRITICAL(); return (OS_NO_ERR); 如果没有任务等待该消息队列中的消息,而且此时消息队列未满L11.23(5),指向该消息的指针被插入到消息队列中L11.23(6)。这样,下一个调用OSQPend()函数的任务就可以马上得到该消息。

30、注意,如果此时消息队列已满,那么该消息将由于不能插入到消息队列中而丢失。此外,如果OSQPost()函数是由中断服务子程序调用的,那么即使产生了更高优先级的任务,也不会在调用OSSched()函数时发生任务切换。这个动作一直要等到中断嵌套的最外层中断服务子程序调用OSIntExit()函数时才能进行(见3.09节,C/OS-II中的中断)。11.04向消息队列发送一个消息(后进先出LIFO),OSQPostFront()OSQPostFront()函数和OSQPost()基本上是一样的,只是在插入新的消息到消息队列中时,使用.OSQOut作为指向下一个插入消息的单元的指针,而不是.OSQIn。程序清单 L11.24是它的源代码。值得注意的是,.OSQOut指针指向的是已经插入了消息指针的单元,所以再插入新的消息指针前,必须先将.OSQOut指针在消息队列中前移一个单元。如果.OSQOut指针指向的当前单元是队列中的第一个单元L11.

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1