null嵌入式系统开发嵌入式系统开发第六章
同步、互斥与通信第六章
同步、互斥与通信主要内容主要内容概述
信号量
邮箱和消息队列
事件
异步信号*
管道*概述概述多任务系统中任务之间/任务与ISR的关系
相互独立 仅竞争CPU资源
竞争除CPU外的其他资源(互斥)
同步 协调彼此运行的步调,保证协同运行的各个任务具有正确的执行次序
通信 彼此间传递数据或信息,以协同完成某项工作概述任务能以以下方式与中断处理程序或其他任务进行同步或通信:
单向同步或通信:一个任务与另一个任务或一个ISR同步或通信。
双向同步或通信:两个任务相互同步或通信。双向同步不能在任务与ISR之间进行,因为ISR不能等待。 概述null
ISR xTask yPOSTPEND任务与ISR之间的同步(单向)Task xTask yPOSTPENDPOSTPEND任务与任务之间的同步(双向)任务与任务之间的同步(单向)Task xTask yPOSTPEND概述在嵌入式多任务系统中,任务间的耦合程度是不一样的:
耦合程度较高:任务之间需要进行大量的通信,相应的系统开销较大;
耦合程度较低:任务之间不存在通信需求,其间的同步关系很弱甚至不需要同步或互斥,系统开销较小。
研究任务间耦合程度的高低对于合理地设计应用系统、划分任务有很重要的作用。 概述概述在单处理器平台上,嵌入式操作系统内核提供的同步、互斥与通信机制主要包括:
信号量(semaphore),用于互斥与同步
事件(组)(event group),用于同步
异步信号(asynchronous signal),用于同步
邮箱(mailbox)、消息队列(message queue),用于消息通信
管道(pipe),提供非结构化数据交换和实现同步 概述概述以下一些机制也可用于同步与通信(在单处理器或多处理器系统中):
全局变量
共享内存
Sockets
远程过程调用(Remote Procedure Call)概述第一节
信号量第一节
信号量信号量的种类及用途
互斥信号量
二值信号量
计数信号量
信号量机制的主要数据结构
典型的信号量操作信号量的种类及用途信号量用于实现任务与任务之间、任务与中断处理程序之间的同步与互斥。
信号量一般分为三种:信号量的种类及用途用于解决互斥问题。它比较特殊,可能会引起优先级反转问题。用于解决同步问题用于解决资源计数问题将信号量进行种类细分,可以根据其用途,在具体
实现时做专门处理,提高执行效率和可靠性。互斥信号量用互斥信号量保护的代码区称作“临界区”,临界区代码通常用于对共享资源的访问。
互斥信号量的值被初始化成1,表明目前没有任务进入“临界区”,但最多只有一个任务可以进入“临界区”。
第一个试图进入“临界区”的任务将成功获得互斥信号量,而随后试图进入用同一信号量保护的临界区的所有其他任务就必须等待。
当任务离开“临界区”时,它将释放信号量并允许正在等待该信号量的任务进入“临界区”。互斥信号量互斥信号量互斥信号量共享资源可能是一段存储器空间、一个数据结构或I/O设备,也可能是被两个或多个并发任务共享的任何内容。
使用互斥信号量可以实现对共享资源的串行访问,保证只有成功地获取互斥信号量的任务才能够释放它。
互斥信号量是一种特殊的二值信号量,一般它支持所有权、递归访问、任务删除安全和一些避免优先级反转、饥饿、死锁等互斥所固有问题的协议。 互斥信号量状态图互斥信号量状态图互斥信号量状态图开启锁定初始化
值为1申请并获得
值为0释放
值为1申请(递归)并获得
锁定数加1释放(递归)
锁定数减1互斥信号量互斥信号量所有权:当一个任务通过获取互斥信号量而将其锁定时,得到该互斥信号量的所有权。相反,当一个任务释放信号量时,失去对其的所有权。
当一个任务拥有互斥信号量时,其他的任务不能再锁定或释放它,即任务要释放互斥信号量,必须事前先获取该信号量。 互斥信号量Task1RoutineARoutineB互斥信号量嵌套(递归)资源访问
如果Task1调用RoutineA,而RoutineA又调用RoutineB,并且三者访问相同的共享资源,就发生了递归共享资源的访问同步问题。 一个递归的互斥信号量允许嵌套锁定互斥信号量,而不引起死锁。 互斥信号量互斥信号量嵌套(递归)资源访问
每个获取信号量的调用必须与释放信号量的调用相匹配。当最外层的获取信号量的调用与释放信号量的调用匹配时,该信号量才允许被其它任务访问。
用于同步的信号量不支持嵌套访问,任务如果对同步信号量使用上述操作是错误的,任务会被永久阻塞,并且阻塞条件永远不会解除。 互斥信号量互斥信号量删除安全:
在一个受信号量保护的临界区,经常需要保护在临界区执行的任务不会被意外地删除。
删除一个在临界区执行的任务可能引起意想不到的后果,造成保护资源的信号量不可用,可能导致资源处于破坏状态,也就导致了其它所有要访问该资源的任务无法得到满足。 互斥信号量互斥信号量删除安全:
为避免任务在临界区执行时不被意外删除:
提供“任务保护”和“解除任务保护”原语对
同时,为互斥信号量提供“删除安全”选项。在创建信号量的时候使用这个选项,当应用每次获取信号量时隐含地使能“任务保护”功能,当每次释放信号量时隐含地使用“解除任务保护”功能。 各种互斥机制比较各种互斥机制比较二值信号量二值信号量二值信号量主要用于任务与任务之间、任务与中断服务程序之间的同步
用于同步的二值信号量初始值为0,表示同步事件尚未产生;
任务申请信号量以等待该同步事件的发生;
另一个任务或ISR到达同步点时,释放信号量(将其值设置为1)表示同步事件已发生,以唤醒等待的任务。二值信号量二值信号量二值信号量状态图可获得不可获得申请并获得
(值为0)释放
(值为1)初始化
值为0nullTask1()
{
……
执行一些操作;
将信号量sem1置1;
申请信号量sem2;
……
……
}Task2()
{
……
申请信号量sem1;
执行一些操作;
将信号量sem2置1;
……
……
}Task2申请信号量sem1失败,系统切换到Task1sem1被置1后,Task2得到sem1并抢占Task1Task2运行到某处时因某种原因被阻塞,系统切换到Task1用二值信号量实现两个任务之间的双向同步
Task2优先级高于Task1
sem1和sem2的初始值均为0计数信号量计数信号量计数信号量用于控制系统中共享资源的多个实例的使用,允许多个任务同时访问同一种资源的多个实例
计数信号量被初始化为n(非负整数),n为该种共享资源的数目。计数信号量计数信号量计数信号量状态图可获得不可获得初始化
值大于0申请并获得
值为0释放
值为1申请并获得
值减1释放
值加1计数信号量计数信号量计数信号量使用实例:有界缓冲问题null生产者任务
do
{
…
产生一个数据项
…
申请empty
申请mutex
…
将新生成的数据项添加到缓冲中
…
释放mutex
释放full
} while (1);消费者任务
do
{
申请full
申请mutex
…
从缓冲中移出一个数据项的内容
…
释放mutex
释放empty
…
消费新获得的数据项内容
…
} while (1); 计数信号量full:已被填充的数据项数目,取值范围0-n,初始值为0
计数信号量empty:空闲数据项数目,取值范围为0-n,初始值为n;
互斥信号量mutex:控制生产者任务和消费者任务对有界缓冲的访问,初始值为1。信号量机制的主要数据结构信号量机制的主要数据结构信号量机制的主要数据结构信号量机制的主要数据结构信号量控制块:管理所有创建的信号量,内核在系统运行时动态分配和回收信号量控制块
互斥和二值信号量控制块结构: Binary_Semaphore_Control_Blockwait_queue 任务等待队列
attributes 信号量属性
lock_nesting_behavior试图嵌套获得时的规则
wait_discipline 任务等待信号量的方式
priority_ceiling 优先级天花板值
lock 是否被占有
holder 拥有者
nest_count 嵌套层数null计数信号量控制结构Counting_Semaphore_Control_Block
wait_queue 任务等待队列
attributes 计数信号量属性
maximum_count 最大计数值
wait_discipline 任务等待信号量的方式
count 当前计数值
信号量机制的主要数据结构信号量内部实现机制实例说明
-µC/OS-II信号量内部实现机制实例说明
-µC/OS-II事件控制块ECB-同步与通信机制的基本数据结构
typedef struct{
INT8U OSEventType;//事件类型
INT8U OSEventGrp;//等待任务所在的组
INT16U OSEventCnt;//计数器(信号量)
void *OSEventPtr;//指向消息或消息队列的指针
INT8U OSEventTbl[OS_EVENT_TBL_SIZE];//等待任务列表
}OS_EVENT;信号量内部实现机制实例说明
-µC/OS-II信号量内部实现机制实例说明
-µC/OS-II当一个事件发生后,等待事件列表中优先级最高的任务(即在.OSEventTbl[]&OSEventGrp中所有被置1的位中优先级数值最小的任务)得到该事件。信号量内部实现机制实例说明
-µC/OS-II信号量内部实现机制实例说明
-µC/OS-II当.OSEventTbl[n]中的任何一位为1时,OSEventGrp中的第n位为1。
与任务就绪列表类似!信号量内部实现机制实例说明
-µC/OS-II信号量内部实现机制实例说明
-µC/OS-II将一个任务插入到等待事件的任务列表中:
pevent->OSEventGrp
|= OSMapTbl[prio >> 3];
pevent->OSEventTbl[prio >> 3]
|= OSMapTbl[prio & 0x07];
与将一个任务插入到就绪列表中的操作类似!Index Bit mask (Binary)
0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 1 0
2 0 0 0 0 0 1 0 0
3 0 0 0 0 1 0 0 0
4 0 0 0 1 0 0 0 0
5 0 0 1 0 0 0 0 0
6 0 1 0 0 0 0 0 0
7 1 0 0 0 0 0 0 0信号量内部实现机制实例说明
-µC/OS-II信号量内部实现机制实例说明
-µC/OS-II从等待事件的任务列表中使任务脱离等待状态
if ((pevent->OSEventTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) {
pevent->OSEventGrp &= ~OSMapTbl[prio >> 3];
}
与将任务从就绪列表中清除的操作类似!信号量内部实现机制实例说明
-µC/OS-II信号量内部实现机制实例说明
-µC/OS-II在等待事件的任务列表中查找优先级最高的任务
y = OSUnMapTbl[pevent->OSEventGrp];
x = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (y << 3) + x;
与查找优先级最高的就绪任务的操作类似!信号量内部实现机制实例说明
-µC/OS-II信号量内部实现机制实例说明
-µC/OS-II空闲事件控制块链表典型的信号量操作典型的信号量操作创建信号量
获取(申请)信号量
释放信号量
删除信号量
清除信号量的任务等待列表
获取有关信号量的各种信息 创建信号量创建信号量功能:根据应用传递的参数创建一个信号量
参数:信号量的名字、属性和初始值等。
内核动作:
从空闲信号量控制块链中分配一个信号量控制块,并初始化信号量属性。
创建成功时,为其分配唯一的ID号返回给应用。
如果已创建信号量数量已达到用户配置的最大数量,就返回错误。创建信号量创建信号量信号量的属性包括:
类型
任务等待信号量的方式(即排列的顺序)
与任务删除安全、递归访问以及解决优先级反转的策略相关的参数(只针对互斥信号量)。
创建信号量创建信号量信号量的属性信号量的类型互斥信号量(MUTEX_SEMAPHORE)计数信号量(COUNTING_SEMAPHORE)二值信号量(BINARY_SEMAPHORE)任务等待信号量的方式先进先出(FIFO)顺序优先级(PRIORITY)顺序优先级反转问题的解决方法(只适用于互斥信号量)优先级继承算法(INHERIT_PRIORITY)优先级天花板算法(PRIORITY_CEILING) ,需给出所有可能获得此信号量的任务中优先级最高的任务的优先级。创建一个信号量OSSemCreate()创建一个信号量OSSemCreate()OS_EVENT *OSSemCreate (INT16U cnt)
{
OS_EVENT *pevent;
pevent = OSEventFreeList;//从空闲事件控制块链中取得一个ECB
if (OSEventFreeList != (OS_EVENT *)0) {
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
if (pevent != (OS_EVENT *)0) { //初始化ECB的各个域
pevent->OSEventType = OS_EVENT_TYPE_SEM; //事件类型为信号量
pevent->OSEventCnt = cnt; //信号量的初始计数值
pevent->OSEventPtr = (void *)0;
OS_EventWaitListInit(pevent); //初始化等待任务列表
}
return (pevent); //调用者需检查返回值,如果为NULL则表示建立失败
}获取(申请)信号量获取(申请)信号量功能:试图获得应用指定的信号量。
if 信号量的值大于0
then 将信号量的值减1
else 根据接收信号量的选项,将任务放到等待队列中,或是直接返回获取(申请)信号量获取(申请)信号量当所申请的信号量不能被立即获得时,可以有以下几种选择:
永远等待
不等待,立即返回,并返回一个错误状态码
指定等待时限(可有效避免死锁)
注意:
不允许在ISR中选择等待
当任务选择等待时,将被按FIFO或优先级顺序放置在等待队列中获取(申请)信号量获取(申请)信号量如果任务等待一个使用优先级继承算法的互斥信号量,且它的优先级高于当前正占有此信号量的任务的优先级,那么占有信号量的任务将继承这个被阻塞的任务的优先级。
如果任务成功地获得一个采用优先级天花板算法的互斥信号量,它的优先级又低于优先级天花板,那么它的优先级将被抬升至天花板。 获取(等待)一个信号量OSSemPend()获取(等待)一个信号量OSSemPend()void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
if (pevent->OSEventCnt > 0) { //信号量值大于0,成功获得信号量并返回
pevent->OSEventCnt--;
*err = OS_NO_ERR;
return;}
OSTCBCur->OSTCBStat |= OS_STAT_SEM; //设置任务状态为等待信号量
OSTCBCur->OSTCBDly = timeout; //设置等待时限
OS_EventTaskWait(pevent);//将任务放置到信号量的等待列表中
OS_Sched(); //内核实施任务调度,系统切换到另一就绪任务执行
if (OSTCBCur->OSTCBStat & OS_STAT_SEM) { //判断任务恢复执行的原因,如果等待时限超时但仍然未获得信号量,则返回超时信息
OSEventTO(pevent);
*err = OS_TIMEOUT;
return;}
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
*err = OS_NO_ERR; //任务由于获得信号量而恢复执行,本调用成功返回
}获取(无等待地请求)一个信号量OSSemAccept()获取(无等待地请求)一个信号量OSSemAccept()INT16U OSSemAccept (OS_EVENT *pevent)
{
INT16U cnt;
cnt = pevent->OSEventCnt;
if (cnt > 0) {
pevent->OSEventCnt--;
}
return (cnt);
}注意:即使不能成功获得信号量(返回值为0),调用者也不会被阻塞。此函数可以在中断处理程序中使用。释放信号量释放信号量功能:释放一个应用指定的信号量。
if 没有任务等待这个信号量
then 信号量的值加1
else 将信号量分配给一个等待任务(将相应的任务移出等待队列,使其就绪)
如果使用了优先级继承或优先级天花板算法,那么执行该功能(系统调用)的任务的优先级将恢复到原来的高度。
释放一个信号量OSSemPost()释放一个信号量OSSemPost()INT8U OSSemPost (OS_EVENT *pevent)
{
if (pevent->OSEventGrp!=0x00) { //如果有任务在等待该信号量
OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM); //使等待任务列表中优先级最高的任务就绪
OS_Sched(); //内核实施任务调度
return (OS_NO_ERR);//成功返回
}
if (pevent->OSEventCnt < 65535) {//如果没有任务等待该信号量,并且信号量的值未溢出
pevent->OSEventCnt++; //信号量的值加1
return (OS_NO_ERR);//成功返回
}
return (OS_SEM_OVF);//信号量溢出
}删除信号量删除信号量功能:从系统中删除应用指定的一个信号量
内核动作:将信号量控制块返还给系统
删除信号量的不一定是创建信号量的任务
如果有任务正在等待获得该信号量,执行此功能将使所有等待这个信号量的任务回到就绪队列中,且返回一个状态码指示该信号量已被删除删除信号量删除信号量企图获取已删除的信号量将返回一个错误;
在互斥信号量正被使用时(已经被某任务获取),不能删除它。因为该信号量正在保护一个共享资源或临界代码段,该动作可能造成数据崩溃或其他严重问题。 删除一个信号量OSSemDel()删除一个信号量OSSemDel()OS_EVENT *OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *err)
{
BOOLEAN tasks_waiting;
if(pevent->OSEventGrp!=0x00{//根据是否有任务在等待信号量设置等待标志
tasks_waiting=TRUE;
}else{
tasks_waiting=FALSE;
}
switch(opt){
case OS_DEL_NO_PEND://如果有任务等待信号量则不删除信号量
if(task_waiting==FALSE{//没有任务等待,释放ECB回空闲链
pevent->OSEventType=OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr=OSEventFreeList;
OSEventFreeList=pevent;//调整空闲ECB链头指针
*err=OS_NO_ERR;
return((OS_EVENT)0);
}else{ *err=OS_ERR_TASK_WAITING;//有任务等待,删除信号量失败
return(pevent);
}删除一个信号量OSSemDel()删除一个信号量OSSemDel() case OS_DEL_ALWAYS://无论有无任务等待都删除信号量
//将等待列表中的每个任务都设置成就绪
while(pevent->OSEventGrp!=0x00){
OS_EventTaskRdy(pevent,(void *)0, OS_STAT_SEM);}
//释放该信号量的ECB回空闲控制块链
pevent->OSEventType=OS_EVENT_TYPE_UNUSED;
pevent->OSEventFreeList;
OSEventFreeList=pevent;
//如果之前有任务等待信号量,内核实施任务调度
if(tasks_waiting==TRUE){OS_Sched();}
*err=OS_NO_ERR;
return((OS_EVENT *)0);
default:
*err=OS_ERR_INVALID_OPT;
return(pevent);
}
}清除信号量的任务等待列表清除信号量的任务等待列表为了清除等待一个信号量的所有任务,某些内核支持Flush操作,以便释放信号量等待任务列表中的所有任务。当多个任务的执行必须在某些点相遇时,需要这样的机制。 第二节
邮箱和消息队列第二节
邮箱和消息队列通信方式概述
消息队列机制的主要数据结构
典型的消息队列操作
消息队列的其他典型使用通信方式概述任务间的通信方式
直接通信。在通信过程中双方必须明确地知道(命名)彼此:
Send (P,message) – 发送一个消息到任务P
Receive(Q,message) – 从任务Q接收一个消息
间接通信。通信双方不需要指出消息的来源或去向,而通过中间机制来通信。如:
send(A,message) – 发送一个消息给邮箱A
receive(A,message) – 从邮箱A接收一个消息通信方式概述概述消息队列:属于间接通信方式
消息:内存空间中一段长度可变的缓冲区,其长度和内容均可以由用户定义,其内容可以是实际的数据、数据块的指针或空。
对消息内容的解释由应用完成。
从操作系统观点看,消息没有定义的格式,所有的消息都是字节流,没有特定的含义。
从应用观点看,根据应用定义的消息格式,消息被解释成特定的含义。
应用可以只把消息当成一个标志,这时消息机制用于实现同步概述概述一些操作系统内核把消息进一步分为:邮箱和消息队列
邮箱仅能存放单条消息,它提供了一种低开销的机制来传送信息。每个邮箱可以保存一条大小为若干个字节的消息。
消息队列可存放若干消息,提供了一种任务间缓冲通信的方法。
消息机制可支持定长与可变长度两种模式的消息,可变长度的消息队列需要对队列中的每一条消息增加额外的存储开销。概述消息队列机制的主要数据结构消息队列机制的主要数据结构消息队列及其相关的参数和支持数据结构 消息队列状态图消息队列状态图非空满队列创建
消息数为0消息队列状态图消息发送
消息数加1空消息发送
消息数为1消息接收
消息数为0消息接收
消息数减1消息接收
消息数减1消息发送
消息数等于队列长度消息队列机制的主要数据结构消息队列机制的主要数据结构消息队列控制块
管理所有创建的消息队列,系统运行时动态分配和回收消息队列控制块
消息队列缓冲区
存放发送到该队列的消息,接收者从缓冲区中取出消息。
消息的发送或接收有两种方法(影响消息缓冲区结构):
将数据从发送任务的空间完全拷贝到接收任务的空间中(效率较低,执行时间与消息大小有关)
只传递指向数据存储空间的指针(提高系统性能)null发送和接收消息的消息拷贝和内存使用
这种消息传递方法效率低、占用空间大
一种效率更高的方式是传递消息指针消息队列机制的主要数据结构number_of_messagemax_message_count消息队列控制块消息队列缓冲区消息队列机制的主要数据结构消息队列机制的主要数据结构消息队列的环形缓冲消息队列机制的主要数据结构max_message_countqueue_endqueue_startqueue_outnumber_of_messagequeue_in消息指针典型的消息队列操作典型的消息队列操作创建消息队列
发送普通消息
发送紧急消息
发送广播消息
接收消息
删除消息队列
获取有关消息队列的各种信息 创建消息队列创建消息队列创建消息队列时,调用者可以指定如下参数:
消息的最大长度
每个消息队列中最多的消息数
消息队列的属性
任务等待消息时的排队方式:FIFO或PRIORITY
系统为新创建的消息队列分配唯一的ID 发送消息发送消息根据紧急程度的不同,消息通常可分为普通消息与紧急消息。
如果有任务正在等待消息(即消息队列为空),则普通消息发送和紧急消息发送的执行效果是一样的。任务从等待队列移到就绪队列中,消息被拷贝到任务提供的缓冲区中(或者由接收任务得到指向消息的指针)。
如果没有任务等待,发送普通消息将消息放在队列尾,而发送紧急消息将消息放在队列头。发送消息发送消息发送消息发送消息如果发送消息时队列已被填满,则不同的操作系统可能采取不同的处理办法:
挂起试图向已满的消息队列中发送消息的任务(不适用于中断服务程序)
简单地丢弃该条消息并向调用者返回错误信息
广播消息。在此之前所有试图从队列中接收消息的任务此时都将获得相同的消息。该功能拷贝消息到各任务的消息缓冲中(或者让所有的等待任务得到指向消息的指针),并唤醒所有的等待任务。接收消息接收消息如果指定的消息队列中有消息,则将其中的第一条消息拷贝到调用者的缓冲区(或者将第一条消息指针传递给调用者),并从消息队列中删除它。
如果此时消息队列中没有消息,则可能出现以下几种情况:
永远等待消息的到达:等待消息的任务按FIFO或优先级高低顺序排列在等待队列中
等待消息且指定等待时限:等待消息的任务按FIFO或优先级高低顺序排列在等待队列中
不等待,强制立即返回接收消息接收消息限时等待可有效预防死锁
中断服务程序接收消息时必须选择不等待,因为中断服务程序是不能被阻塞的。
如果消息队列被应用删除,则所有等待该消息队列的任务都被返回一个错误信息,并回复到就绪状态。接收消息接收消息删除消息队列删除消息队列从系统中删除指定的消息队列,释放消息队列控制块及消息队列缓冲区。
任何知道此消息队列ID号的代码都可以删除它。
消息队列被删除后,所有等待从这个消息队列接收消息的任务都回到就绪态,并得到一个错误信息表明消息队列已被删除。消息队列的其他典型使用消息队列的其他典型使用紧耦合的单向数据通信:发送任务发送消息后要求一个响应信号,表明接收任务已经成功接收到消息。 消息队列的其他典型使用消息队列的其他典型使用紧耦合的双向数据通信 :如果数据需要在任务之间双向流动,则可以采用紧耦合的双向数据通信模式(也称为全双工通信)。 第三节
事 件第三节
事 件概述
事件机制的主要数据结构
典型的事件操作
事件机制的典型应用概述在嵌入式实时内核中,事件是指一种表明预先定义的系统事件已经发生的机制。
事件机制用于任务与任务之间、任务与ISR之间的同步。其主要的特点是可实现一对多的同步。
一个事件就是一个标志,不具备其它信息。
一个或多个事件构成一个事件集。事件集可以用一个指定长度的变量(比如一个8bit, 16bit或32bit的无符号整型变量,不同的操作系统其具体实现不一样)来表示,而每个事件由在事件集变量中的某一位来代表。 概述概述事件及事件集有以下特点:
事件间相互独立
事件仅用于同步,不提供数据传输功能
事件无队列,即多次发送同一事件,在未经过任何处理的情况下,其效果等同于只发送一次。
提供事件机制的意义在于:
当某任务要与多个任务或中断服务同步时,就需要使用事件机制。
若任务需要与一组事件中的任意一个发生同步,可称为独立型同步(逻辑“或”关系)。
任务也可以等待若干事件都发生时才同步,称为关联型同步(逻辑“与”关系)。 概述null“或”同步和“与”同步概述任务任务任务任务ISRISRORAND“与”型同步“或”型同步事件集事件集POSTPOSTPENDPENDnull用多个事件的组合发信号给多个任务概述任务任务任务ISRORAND事件集事件集事件集
(8,16或32位)POSTPENDPENDnull术语:
发送事件集 。指在一次发送过程中发往接收者(比如任务)的一个或多个事件的组合。
待处理事件集。指已被发送到一个接收者但还没有被接收(即正在等待处理)的所有事件的集合。
事件条件。指事件接收者在一次接收过程中期待接收的一个或多个事件的集合。
“或”同步:待处理事件集只要包括事件条件中的任一事件即可满足要求;
“与”同步:其二是待处理事件集必须包括事件条件中的全部事件方可满足要求。 概述事件机制的主要数据结构事件机制的主要数据结构事件集控制块:管理所有创建的事件集
或者
事件集附属于任务,不需创建,其相关参数成为任务控制块的一部分事件的内部实现机制实例说明
-µC/OS-II事件的内部实现机制实例说明
-µC/OS-II事件标志组数据结构 OS_FLAG_GRP
typedef struct{
INT8U OSFlagType;//指示本数据结构的类型
void *OSFlagWaitList;//等待事件标志的任务链表
OS_FLAGS OSFlagFlags;//各事件标志的当前状态
}OS_FLAG_GRP;
事件标志节点数据结构 OS_FLAG_NODE
typedef struct{
void *OSFlagNodeNext;//后驱指针
void *OSFlagNodePrev;//前驱指针
void *OSFlagNodeTCB;//任务控制块指针
void *OSFlagNodeFlagGrp;//指回OS_FLAG_GRP结构
OS_FLAGS OSFlagNodeFlags;//所等待的事件标志组合
INT8U OSFlagNodeWaitType;//等待类型(与、或)
}OS_FLAG_NODE;事件标志组、事件标志节点及
任务控制块之间的关系事件标志组、事件标志节点及
任务控制块之间的关系OS_FLAG_GRPOS_FLAG_NODE.OSTCBFlagNode
.OSFlagNodeFlags
.OSFlagNodeWaitType
.OSFlagNodeNext
.OSFlagNodePrev
.OSFlagNodeTCB.OSFlagWaitList
.OSFlagFlags
.OSFlagTypeAND or ORAND or ORAND or OROS_EVENT_TYPE_FLAG00.OSTCBFlagNodeOS_TCB典型的事件操作典型的事件操作创建事件集
删除事件集
发送事件(集)
接收事件(集)
获取有关事件集的各种信息 创建事件集创建事件集申请空闲事件集控制块,设置事件集属性,初始化控制块中的域,分配ID号创建一个事件标志组OSFlagCreate()创建一个事件标志组OSFlagCreate()OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags, INT8U *err)
{
OS_FLAG_GRP *pgrp;
pgrp=OSFlagFreeList;//获取一个空闲事件标志组结构
if(pgrp!=(OS_FLAG_GRP *)0){//获取成功,初始化该结构中的域
OSFlagFreeList=(OS_FLAG_GRP *)OSFlagFreeList->OSFlagWaitList;//调整空闲结构链头指针
pgrp->OSFlagType=OS_EVENT_TYPE_FLAG;
pgrp->OSFlagFlags=flags;//初始化当前各事件标志的状态
pgrp->OSFlagWaitList=(void *)0;//尚无任务等待事件标志
*err=OS_NO_ERR;
}else{*err=OS_FLAG_GRP_DEPLETED;}
return(pgrp);
}接收事件(集)接收事件(集)在接收事件(集)时可以有如下选项接收(等待)事件标志组的事件标志位OSFlagPend()接收(等待)事件标志组的事件标志位OSFlagPend()OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *err)
{
OS_FLAG_NODE node;// OS_FLAG_NODE作为局部变量存在于调用该函数的任务堆栈中
OS_FLAGS flags_cur;
OS_FLAGS flags_rdy;
switch(wait_type){
case OS_FLAG_WAIT_SET_ALL://任务以“与”方式等待事件标志
flags_rdy=pgrp->OSFlagFlags&flags;
if(flags_rdy==flags){//事件标志当前状态与等待条件相符
pgrp->OSFlagFlags&=~flags_rdy;//清除(即“消费”)满足条件的事件标志
flags_cur=pgrp->OSFlagFlags;
*err=OS_NO_ERR;
return(flags_cur);//返回处理后的事件标志组
}else{OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);}
//事件标志当前状态与等待条件不相符,任务被阻塞
break;接收(等待)事件标志组的事件标志位OSFlagPend()接收(等待)事件标志组的事件标志位OSFlagPend() case OS_FLAG_WAIT_SET_ANY: //任务以“或”方式等待事件标志
flags_rdy=pgrp->OSFlagFlags&flags;
if(flags_rdy!=(OS_FLAGS)0){//有满足条件的事件标志
pgrp->OSFlagFlags&=~flags_rdy; //清除(即“消费”)满足条件的事件标志
flags_cur=pgrp->OSFlagFlags;
*err=OS_NO_ERR;
return(flags_cur); //返回处理后的事件标志组
}else{OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);}
//事件标志当前状态与等待条件不相符,任务被阻塞
break;
default:
flags_cur=(OS_FLAGS)0;
*err=OS_FLAG_ERR_WAIT_TYPE;
return(flags_cur);
}接收(等待)事件标志组的事件标志位OSFlagPend() OS_Sched();//当前任务被放到事件标志等待链后,内核实施任务调度
if(OSTCBCur->OSTCBStat & OS_STAT_FLAG){//判断任务重新就绪的原因,如果是等待超时
OS_FlagUnlink(&node);//将任务从事件标志等待链中解除下来
OSTCBCur->OSTCBStat=OS_STAT_RDY;//设置当前任务状态为就绪
flags_cur=(OS_FLAGS)0;//无效的事件标志状态
*err=OS_TIMEOUT;//超时信号
}else{//任务重新就绪的原因是在限定时间得到了满足条件的事件标志
pgrp->OSFlagFlags&=~OSTCBCur->OSTCBFlagsRdy; //清除(即“消费”)满足条件的事件标志
flags_cur=pgrp->OSFlagFlags;
*err=OS_NO_ERR;
}
return(flags_cur);
}接收(等待)事件标志组的事件标志位OSFlagPend()添加一个任务到事件标志组等待任务链表中OS_FlagBlock()添加一个任务到事件标志组等待任务链表中OS_FlagBlock()OS_FLAG_GRPOS_EVENT_TYPE_FLAGAND or ORAND or OR00OS_TCBOS_TCBOSTCBCurOS_FLAG_NODE0接收(无等待地获取)事件标志OSFlagAccept()接收(无等待地获取)事件标志OSFlagAccept()OS_FLAGS OSFlagAccept (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT8U *err)
{
OS_FLAGS flags_cur, flags_rdy;
*err = OS_NO_ERR;
switch (wait_type) {//判断等待事件标志的方式
case OS_FLAG_WAIT_SET_ALL://”与”方式等待
flags_rdy = pgrp->OSFlagFlags & flags;
if (flags_rdy == flags) pgrp->OSFlagFlags &= ~flags_rdy;//事件标志当前状态与等待条件相符,清除(即“消费”)相应的事件标志
else *err = OS_FLAG_ERR_NOT_RDY;//不符合条件,返回错误信息
flags_cur = pgrp->OSFlagFlags;
break;接收(无等待地获取)事件标志OSFlagAccept()接收(无等待地获取)事件标志OSFlagAccept() case OS_FLAG_WAIT_SET_ANY://”或”方式等待
flags_rdy = pgrp->OSFlagFlags & flags;
if (flags_rdy != (OS_FLAGS)0)
pgrp->OSFlagFlags &= ~flags_rdy;//事件标志当前状态与等待条件相符,清除(即“消费”)相应的事件标志
else *err = OS_FLAG_ERR_NOT_RDY; //不符合条件,返回错误信息
flags_cur = pgrp->OSFlagFlags;
break;
default:
flags_cur = (OS_FLAGS)0;//0表示无效的事件标志组
*err = OS_FLAG_ERR_WAIT_TYPE;//错误的等待类型
break;
}
return (flags_cur);
}发送事件(集)发送事件(集)调用者(任务或中断)构造一个事件(集),将其发往接收者(比如目标任务)。可能会出现以下几种情况之一:
目标任务正在等待的事件条件得到满足,任务就绪;
目标任务正在等待的事件条件没有得到满足,该事件(集)被按“或”操作,保存到目标任务的待处理事件集中,目标任务继续等待;
目标任务未等待事件(集),该事件(集)被按“或”操作,保存到目标任务的待处理事件集中。 发送(置位)事件标志组中的事件标志OSFlagPost()发送(置位)事件标志组中的事件标志OSFlagPost()OS_FLAGS OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U *err)
{
OS_FLAG_NODE *pnode;
BOOLEAN sched= FALSE;//初始化调度标志
OS_FLAGS flags_cur, flags_rdy;
pgrp->OSFlagFlags |= flags;//置位事件标志
pnode = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;//获取任务等待链头节点
while (pnode != (OS_FLAG_NODE *)0) {//如果有任务等待,遍历等待链
switch (pnode->OSFlagNodeWaitType) {
case OS_FLAG_WAIT_SET_ALL://”与”方式等待
flags_rdy = pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy == pnode->OSFlagNodeFlags) {//符合等待条件
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE)
sched = TRUE;//如果任务就绪,设置调度标志
}
break;发送(置位)事件标志组中的事件标志OSFlagPost() case OS_FLAG_WAIT_SET_ANY://”或”方式等待
flags_rdy = pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy != (OS_FLAGS)0) {//有满足条件的事件标志
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE)
sched = TRUE; //如果任务就绪,设置调度标志
}
break;
}
pnode = (OS_FLAG_NODE *)pnode->OSFlagNodeNext;//下一个等待事件标志的节点
}
if (sched == TRUE) OS_Sched();//如果设置了调度标志,则实施调度
*err = OS_NO_ERR;
return (pgrp->OSFlagFlags);
}发送(置位)事件标志组中的事件标志OSFlagPost()删除事件集删除事件集回收事件集控制块到空闲链中,等待接收该事件集的任务被恢复就绪删除事件标志组OSFlagDel()删除事件标志组OSFlagDel()OS_FLAG_GRP *OSFlagDel (OS_FLAG_GRP *pgrp, INT8U opt, INT8U *err)
{ BOOLEAN tasks_waiting;
OS_FLAG_NODE *pnode;
if (pgrp->OSFlagWaitList != (void *)0) tasks_waiting = TRUE;//有任务等待
else tasks_waiting = FALSE;//无任务等待
switch (opt) {
case OS_DEL_NO_PEND://在无任务等待时才删除事件标志组
if (tasks_waiting == FALSE) {//无任务等待,释放控制块到空闲链中
pgrp->OSFlagType = OS_EVENT_TYPE_UNUSED;
pgrp->OSFlagWaitList = (void *)OSFlagFreeList;
OSFlagFreeList = pgrp;
*err = OS_NO_ERR;
return ((OS_FLAG_GRP *)0);
} else {//有任务等待,删除失败
*err = OS_ERR_TASK_WAITING;
return (pgrp);
}删除事件标志组OSFlagDel()删除事件标志组OSFlagDel() case OS_DEL_ALWAYS://无论是否有任务等待,都删除事件标志组
pnode = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;//获取等待头节点
while (pnode != (OS_FLAG_NODE *)0) {//遍历整个等待任务链,使每个等待任务就绪
OS_FlagTaskRdy(pnode, (OS_FLAGS)0);
pnode = (OS_FLAG_NODE *)pnode->OSFlagNodeNext;
}
pgrp->OSFlagType = OS_EVENT_TYPE_UNUSED;
pgrp->OSFlagWaitList = (void *)OSFlagFreeList;
OSFlagFreeList = pgrp;//释放控制块回空闲链
if (tasks_waiting == TRUE) OS_Sched();//如果之前有任务等待,
*err = OS_NO_ERR; 内核实施调度
return ((OS_FLAG_GRP *)0);
default:
*err = OS_ERR_INVALID_OPT;
return (pgrp);
}
}事件机制的典型应用设置事件②设置事件②Task1ISR来自某设备的中断Task2消息队列Q事件标志集信号量S01000100发送消息①释放信号量①接收消息④获取信号量④③事件机制的典型应用解决复杂的应用设计问题①发送方(Task1或ISR)发送信息(消息或信号量);
②发送方(Task1或ISR)设置相应的事件标志(指示消息或信号量的发送);
③接收方(Task2)检测事件标志集,判断是否满足其接收条件(“与”条件接收或“或”条件接收);
④接收方(Task2)根据事件标志集的指示定向接收信息(消息或信号量),达到和不同发送方(Task1或ISR)同步或通信的目的。 第四节
异步信号第四节
异步信号概述
异步信号机制与中断机制的比较
异步信号机制与事件机制的比较
异步信号机制的主要数据结构
典型的异步信号操作概述异步信号机制用于任务与任务之间、任务与ISR之间的异步操作
本文档为【嵌入式系统开发-Chapter6-同步、互斥_与通信】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。