第6章 6.0 6.1 6.2 6.3 6.4 6.5
任务之间的通讯与同步 .................................................................................... 1 事件控制块ECB ............................................................................................... 2 初始化一个ECB块,OSEVENTWAITLISTINIT() ............................................. 7 使一个任务进入就绪状态,OSEVENTTASKRDY() ......................................... 8 使一个任务进入等待状态, OSEVENTTASKWAIT() .......................................10 由于等待超时将一个任务置为就绪状态, OSEVENTTO()............................11 信号量 .............................................................................................................11
建立一个信号量, OSSemCreate() ......................................................12 等待一个信号量, OSSemPend() ..........................................................13 发送一个信号量, OSSemPost() ..........................................................15 无等待地请求一个信号量, OSSemAccept() ......................................16 查询一个信号量的当前状态, OSSemQuery() ....................................17
6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.6
邮箱 .................................................................................................................18
建立一个邮箱,OSMboxCreate() ........................................................19 等待一个邮箱中的消息,OSMboxPend() ............................................20 发送一个消息到邮箱中,OSMboxPost() ............................................22 无等待地从邮箱中得到一个消息, OSMboxAccept() ........................23 查询一个邮箱的状态, OSMboxQuery() ..............................................24 使用邮箱作为二值信号量 ....................................................................25 使用邮箱实现延时,而不使用OSTimeDly() .....................................26
6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.6.6 6.6.7 6.7
消息队列 .........................................................................................................27
建立一个消息队列,OSQCreate() ......................................................31 等待一个消息队列中的消息,OSQPend() ..........................................33 向消息队列发送一个消息(FIFO),OSQPost() ..............................35 向消息队列发送一个消息(LIFO),OSQPostFront() ....................37 无等待地从一个消息队列中取得消息, OSQAccept() ......................38 清空一个消息队列, OSQFlush() ........................................................39 查询一个消息队列的状态,OSQQuery() ............................................40 使用消息队列读取模拟量的值 ............................................................41 使用一个消息队列作为计数信号量 ....................................................42
6.7.1 6.7.2 6.7.3 6.7.4 6.7.5 6.7.6 6.7.7 6.7.8 6.7.9
I
第6章 任务之间的通讯与同步
在μC/OS-II中,有多种方法可以保护任务之间的共享数据和提供任务之间的通讯。在前面的章节中,已经讲到了其中的两种:
一是利用宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()来关闭中断和打开中断。当两个
任务或者一个任务和一个中断服务子程序共享某些数据时,可以采用这种方法,详见3.00
节 临界段、8.03.02
节
OS_ENTER_CRITICAL() 和
OS_EXIT_CRITICAL()及9.03.02节 临界段,OS_CPU.H;
二是利用函数OSSchedLock()和OSSchekUnlock()对μC/OS-II中的任务调度函数上锁和开
锁。用这种方法也可以实现数据的共享,详见3.06节 给调度器上锁和开锁。 本章将介绍另外三种用于数据共享和任务通讯的方法:信号量、邮箱和消息队列。 图F6.1介绍了任务和中断服务子程序之间是如何进行通讯的。
一个任务或者中断服务子程序可以通过事件控制块ECB(Event Control Blocks)来向另外的任务发信号[F6.1A(1)]。这里,所有的信号都被看成是事件(Event)。这也说明为什么上面把用于通讯的数据结构叫做事件控制块。一个任务还可以等待另一个任务或中断服务子程序给它发送信号[F6.1A(2)]。这里要注意的是,只有任务可以等待事件发生,中断服务子程序是不能这样做的。对于处于等待状态的任务,还可以给它指定一个最长等待时间,以此来防止因为等待的事件没有发生而无限期地等下去。
多个任务可以同时等待同一个事件的发生[F6.1B]。在这种情况下,当该事件发生后,所有等待该事件的任务中,优先级最高的任务得到了该事件并进入就绪状态,准备执行。上面讲到的事件,可以是信号量、邮箱或者消息队列等。当事件控制块是一个信号量时,任务可以等待它,也可以给它发送消息。
6-1
图 6.1 事件控制块的使用
6.0 事件控制块ECB
μC/OS-II通过uCOS_II.H 中定义的OS_EVENT数据结构来维护一个事件控制块的所有信息[程序清单L6.1],也就是本章开篇讲到的事件控制块ECB。该结构中除了包含了事件本身的定义,如用于信号量的计数器,用于指向邮箱的指针,以及指向消息队列的指针数组等,还定义了等待该事件的所有任务的列表。程序清单 L6.1是该数据结构的定义。
6-2
程序清单 L6.1 ECB数据结构 typedef struct {
void *OSEventPtr; /* 指向消息或者消息队列的指针 */ INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任务列表 */ INT16U OSEventCnt; /* 计数器(当事件是信号量时) */ INT8U OSEventType; /* 时间类型 */ INT8U OSEventGrp; /* 等待任务所在的组 */ } OS_EVENT;
.OSEventPtr指针,只有在所定义的事件是邮箱或者消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构,详见6.06节消息邮箱和6.07节消息队列。
.OSEventTbl[] 和 .OSEventGrp 很像前面讲到的OSRdyTbl[]和OSRdyGrp,只不过前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。(见3.04节 就绪表)
.OSEventCnt 当事件是一个信号量时,.OSEventCnt是用于信号量的计数器,(见6.05节信号量)。
.OSEventType定义了事件的具体类型。它可以是信号量(OS_EVENT_SEM)、邮箱(OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。用户要根据该域的具体值来调用相应的系统函数,以保证对其进行的操作的正确性。
每个等待事件发生的任务都被加入到该事件事件控制块中的等待任务列表中,该列表包括.OSEventGrp和.OSEventTbl[]两个域。变量前面的[.]说明该变量是数据结构的一个域。在这里,所有的任务的优先级被分成8组(每组8个优先级),分别对应.OSEventGrp中的8位。当某组中有任务处于等待该事件的状态时,.OSEventGrp中对应的位就被置位。相应地,该任务在.OSEventTbl[]中的对应位也被置位。.OSEventTbl[]数组的大小由系统中任务的最低优先级决定,这个值由uCOS_II.H中的OS_LOWEST_PRIO常数定义。这样,在任务优先级比较少的情况下,减少μC/OS-II对系统RAM的占用量。
当一个事件发生后,该事件的等待事件列表中优先级最高的任务,也即在.OSEventTbl[]中,所有被置1的位中,优先级代码最小的任务得到该事件。图 F6.2给出了.OSEventGrp和.OSEventTbl[]之间的对应关系。该关系可以描述为:
当.OSEventTbl[0]中的任何一位为1时,.OSEventGrp中的第0位为1。 当.OSEventTbl[1]中的任何一位为1时,.OSEventGrp中的第1位为1。
6-3
当.OSEventTbl[2]中的任何一位为1时,.OSEventGrp中的第2位为1。 当.OSEventTbl[3]中的任何一位为1时,.OSEventGrp中的第3位为1。 当.OSEventTbl[4]中的任何一位为1时,.OSEventGrp中的第4位为1。 当.OSEventTbl[5]中的任何一位为1时,.OSEventGrp中的第5位为1。 当.OSEventTbl[6]中的任何一位为1时,.OSEventGrp中的第6位为1。 当.OSEventTbl[7]中的任何一位为1时,.OSEventGrp中的第7位为1。
图 F6.2 事件的等待任务列表 下面的代码将一个任务放到事件的等待任务列表中。
程序清单 L6.2——将一个任务插入到事件的等待任务列表中 pevent->OSEventGrp |= OSMapTbl[prio >> 3]; pevent->OSEventTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
其中,prio是任务的优先级,pevent是指向事件控制块的指针。
6-4
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库第6章 嵌入式实时操作系统UCOS-II(第2版)在线全文阅读。
相关推荐: