uCos-II中任务的同步与通信
任务的同步与通信
任务间的同步
在多任务合作工作过程中,操作系统要解决两个问题:
- 各任务间应该具有一种互斥关系,即对某些共享资源,如果一个任务正在使用,则其他任务只能等待,等到该任务释放资源后,等待任务之一才能使用它;
- 相关的任务在执行上要有先后次序,一个任务要等其伙伴发来通知,或建立了某个条件后才能继续执行,否则只能等待;
任务之间的这种制约性的合作运行机制叫任务间的同步;
任务中的事件
uCosII
使用信号量、消息邮箱和消息队列这些中间环节来实现任务之间的通信,而这些中间环节都统一称为事件;
事件控制块及事件处理
事件控制块
对于事件来说,当其被占用时,会导致其他请求该事件的任务因暂时得不到该事件的服务而处于等待状态;
作为功能完善的事件,应该对这些等待任务有两方面的管理能力:
- 要对等待事件的所有任务进行记录并排序;
- 允许等待任务有一个等待时限,即当等待任务认为等不及时可以退出对事件的请求;
对于等待事件任务的记录,系统定义了一个INT8U
类型的数组OSEventTbl[]
作为等待事件任务的记录表,即等待任务表;
等待任务表以任务优先级别为顺序为每个任务分配一个二进制位,来表示这一位对应的任务为事件的等待任务,否则不是等待任务;
为了加快该表的访问速度,也定义了一个INT8U
类型的变量OSEventGrp
来表示等待任务中的任务组;
等待任务的等待时限记录在等待任务的任务控制块TCB
的成员OSTCBDly
中,并在每个时钟节拍中断服务程序中对该数据进行维护,每当有任务的等待时限到达时,便将该任务从等待任务表中删除,并使它进入就绪状态;
系统为了把描述事件的数据结构统一起来,使用叫做事件控制快ECB
的数据结构来描述诸如信号量、消息邮箱和消息队列等事件;
事件控制块的组织是通过事件控制块链表的形式;
事件处理函数
系统中对事件控制块进行基本操作的函数定义在文件os_core.c
中;
EventWaitListInit()
事件控制块的初始化函数,作用是把变量OSEventGrp
及任务等待表中的每一位都清0,即令事件任务等待表中不含有任何等待任务;OS_EventTaskWait()
把一个任务置于等待状态;OS_EventTaskRdy()
把调用这个函数的任务在任务等待表中的位置清0(解除等待状态)后,再把任务在任务就绪表中对应的位置设置为1,然后引发一次任务调度;OS_EventTO()
使一个等待超时的任务进入就绪状态;
信号量及其操作
当事件控制块成员OSEventType
的值被设置为OS_EVENT_TYPE_SEM
时,这个事件控制块描述的就是一个信号量;
信号量由计数器和等待任务表两部分组成;
操作
OSSemCreate
应用程序在使用信号量之前必须先调用该函数来创建一个信号量;
OSSemPend
请求信号量,为防止任务因得不到信号量而处于长期的等待状态,该函数允许用参数timeout设置一个等待时间的限制,当任务等待时间超过timeout时,可以结束等待状态而进入就绪状态,如果该参数设置为0,则表明任务的等待时间为无限长;
当任务需要访问一个共享资源时,先要请求管理该资源的信号量,这样就可以根据信号量当前是否有效(即信号量计数器的值是否大于0)来决定该任务是否可以继续运行,如果信号量有效,则把信号量计数器减1,然后继续运行任务,如果信号量无效(即信号量计数器的值等于0),则会在等待任务表中把该任务对应的位设置为1而让任务处于等待状态,并把等待时限timeout保存在任务控制块对应的变量中;
如果希望任务在请求信号量,当信号量无效时不进入等待状态而继续运行,则不调用函数OSSemPend()
,而是调用函数OSSemAccept()
来请求信号量;
OSSemPost
任务获得信号量,在访问共享资源结束以后,必须调用该函数释放信号量,也叫发送信号量;
该函数在对信号量的计数器操作之前,要检查是否还有等待该信号量的任务,如果没有,则将信号量计数器值加1,如果有,则调用调度器OS_Sched()
去运行等待任务中优先级最高的任务;
OSSemDel
如果应用程序不需要某个信号,那么可调用该函数来删除该信号;
函数中的参数opt用来指明信号量的删除条件,该参数有两个参数值可以选择:
- OS_DEL_NO_PEND当等待任务表中已没有等待任务时才删除信号量;
- OS_DEL_ALLWAYS在等待任务表中无论是否有等待任务都立即删除信号量;
需要注意,只能在任务中删除信号量,而不能在中断服务程序中删除;
OSSemQuery
任务可以调用该函数随时查询信号量的当前状态;
互斥信号量
互斥信号量时一个二值信号,可以使任务以独占方式使用共享资源;
使用互斥型信号量会出现任务优先级反转的问题,即在可剥夺型内核中,当任务以独占方式使用共享资源时,会出现低优先级任务先于高优先级任务而被运行的现象,这种现象在实时系统中是不允许出现的,因为它破坏了任务执行的预期顺序,可能会导致严重的后果;
解决该现象的办法之一是,使获得信号量任务的优先级别在使用共享资源期间暂时提升到所有任务最高优先级的高一个级别上,以使该任务不被其他任务所打断,从而能尽快地使用完共享资源并释放信号量,然后在释放信号量之后,再恢复该任务原来的优先级别;
OSMutexCreate
创建互斥型信号量,在描述互斥型信号量的事件控制块中,除了成员OSEventType要赋予常数OS_EVENT_TYPE_MUTEX以表明是一个互斥型信号量和仍然没有使用成员OSEventPtr之外,成员OSEventCnt被分成了低8位和高8位两部分,高8位用来存放为了避免出现优先级反转现象而要提升的优先级prio,低8位赋予常数OS_MUTEX_AVAILABLE,表明信号量尚未被任何任务所占用,处于有效状态;
OSMutexPend
请求互斥型信号量;
也可以调用函数OSMutexAccept()无等待地请求一个互斥型信号量;
OSMutexPost
发送一个互斥型信号量;
OSMutexQuery
获取互斥型信号量的当前状态;
OSMutexDel
任务调用该函数可删除一个互斥型信号量;
消息邮箱
OSMboxCreate
创建消息邮箱;
OSMboxPost
向消息邮箱发送消息;
OSMboxPostOpt
向消息邮箱发送消息,可以以广播的方式向事件等待任务表中的所有任务发送消息;
OSMboxPend
请求消息邮箱;
OSMboxAccept
请求消息邮箱失败时,不进行等待而是继续运行;
OSMboxQuery
查询消息邮箱当前状态信息;
OSMboxDel
删除一个消息邮箱;
消息队列
使用消息队列可在任务之间传递多条消息,消息队列由三部分组成:时间控制块、消息队列和消息;
事件控制块成员OSEventPtr指向一个叫做队列控制块(OS_Q)的结构,该结构管理着一个数组MsgTbl[],该数组中的元素都是指向消息的指针;
OSQCreate
创建消息队列,首先需要定义一个指针数组,然后把各个消息数据缓冲区的首地址存入这个数组中,最后调用该函数来创建消息队列;
OSQPend
请求消息队列,即从消息队列中获取消息;
如果希望任务无等待地请求一个消息队列,则可调用函数OSQAccept()函数;
OSQPost/OSQPostFront
向消息队列发送消息;
函数OSQPost()是以FIFO的方式组织消息队列;
函数OSQPostFront()以LIFO的方式组织消息队列;
OSQPostOpt
该函数时任务将以广播的方式通过消息队列发送消息;
OSQFlush
清空消息队列;
OSQDel
删除一个已存在的消息队列;
OSQQuery
查询一个消息队列的状态;