Ucos为了任务之间的通讯定义了信号量,互斥性信号量,消息对象 消息队列等结构以及api,为了统一的管理这些同步,定义了一个结构叫做时间控制块OS_EVENT,如下

typedef struct os_event {

INT8U    OSEventType;

void    *OSEventPtr;

INT16U   OSEventCnt;

OS_PRIO  OSEventGrp;

OS_PRIO  OSEventTbl[OS_EVENT_TBL_SIZE];

#if OS_EVENT_NAME_EN > 0u

INT8U   *OSEventName;

#endif

} OS_EVENT;

OSEventType为事件类型,有信号量类型,互斥性信号量类型,消息邮箱类型,消息队列类型和未定义类型五种

OSEventPtr消息或者消息队列的指针

OSEventCnt 信号量计数器

OSEventTbl 等待任务表

OSEventGrp 等待事件的任务组

该结构我们称之为时间控制体ECB,系统中拥有的全部ECB的数量为

OS_EXT  OS_EVENT          OSEventTbl[OS_MAX_EVENTS];

OS_MAX_EVENTS是在系统配置文件中指定的,也就是编译时决定有几个事件控制体

每个事件控制体内部都有OSEventTbl这个数组, OS_EVENT_TBL_SIZE是一个宏定义,展开之后的定义为((OS_LOWEST_PRIO) / 8u + 1u),和系统就绪表的大小一致

那么正好和OSEventGrp合作起来,用与系统就绪表的方法类似的标识方式作为等待事件的任务标识,具体的解析请查看系统就绪表的说明文章,只介绍一点,当某个任务等待某个已经生成的特定事件的时候,就会在该事件对应的ecb中的OSEventTbl中相应的位置1,并且这个元素对应的OSEventGrp的位置也会置一,当系统在查找等待该事件的任务的时候,就可以直接通过与任务就绪表相同的方式找到等待该事件的最高优先级的任务.如下

y    = OSUnMapTbl[pevent->OSEventGrp];

x    = OSUnMapTbl[pevent->OSEventTbl[y]];

prio = (INT8U)((y << 3u) + x);

这是os_eventtaskready函数的实现可以看到就是直接从表中得到的等待任务的优先级.

其次是OSEventPtr,在系统初始化的时候,他并不是消息或者消息队列的指针,而是操作系统借助OSEventPtr该元素将整个事件控制块数组都连接在了一起,类似于tcb的连接,形成了一个链表,我们叫做空事件控制块链表,如下

OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl));

for (ix = 0u; ix < (OS_MAX_EVENTS - 1u); ix++) {

ix_next = ix + 1u;

pevent1 = &OSEventTbl[ix];

pevent2 = &OSEventTbl[ix_next];

pevent1->OSEventType    = OS_EVENT_TYPE_UNUSED;

pevent1->OSEventPtr     = pevent2;

#if OS_EVENT_NAME_EN > 0u

pevent1->OSEventName    = (INT8U *)(void *)"?";

#endif

}

pevent1                         = &OSEventTbl[ix];

pevent1->OSEventType            = OS_EVENT_TYPE_UNUSED;

pevent1->OSEventPtr             = (OS_EVENT *)0;

#if OS_EVENT_NAME_EN > 0u

pevent1->OSEventName            = (INT8U *)(void *)"?";

#endif

OSEventFreeList                 = &OSEventTbl[0];

#else

OSEventFreeList                 = &OSEventTbl[0];

OSEventFreeList->OSEventType    = OS_EVENT_TYPE_UNUSED;

OSEventFreeList->OSEventPtr     = (OS_EVENT *)0;

这段代码类似于tcb初始化链接的代码,引入了一个新的全局变量OSEventFreeList代表系统空闲事件控制块的指针,连接好了之后,使用时方法如下

pevent = OSEventFreeList;

if (OSEventFreeList != (OS_EVENT *)0) {

OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;

}

创建新的事件的时候,将空事件控制链表的第一个元素拿来存放事件,链表头更新为原来的第二个元素,返回取下来的元素的ecb事件控制体的指针等待用户操作.

系统创建事件相关的函数有四个

OSMboxCreate  创建消息邮箱

OSMutexCreate 创建互斥性信号量

OSQCreate 创建qs消息队列

OSSemCreate 创建信号量

删除事件的函数也对应的有四个,

OSMboxDel 删除消息邮箱

OSMutexDel 删除互斥信号量

OSQDel 删除消息队列

OSSemDel 删除信号量

这个函数都有自己的实现,但是创建函数统一的都调用了一个函数,为

OS_EventWaitListInit(pevent);参数是可用的事件ecb指针

功能很简单,将事件结构体中的OSEventTbl和OSEventGrp清零

接下来应该讲述各个信号的操作了,但是为了更好地讲述,我们需要先来认识几个函数

OS_EventTaskWait

该函数是当一个任务请求了事件但是不能获得的时候将任务登记在时间的等待任务列表中,并把任务控制块的任务等待状态置为非就绪任务,其接受的参数是一个时间控制体的指针,代码如下

OSTCBCur->OSTCBEventPtr               = pevent;

pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;

pevent->OSEventGrp                   |= OSTCBCur->OSTCBBitY;

y             =  OSTCBCur->OSTCBY;

OSRdyTbl[y]  &= (OS_PRIO)~OSTCBCur->OSTCBBitX;

if (OSRdyTbl[y] == 0u) {

OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;

第一句将当前任务的tcb中的事件指针指向传入参数的ecb

二三句相当于在ecb中标识了哪个优先级的任务在等待事件

最后几句是将系统就绪表中当前任务的任务状态设置为非ready状态,此时任务被挂起进入等待任务状态,该函数主要在事件请求中调用,OSXXXPEND()

当任务获取到事件的时候就应当从挂起状态进入到就绪状态,使用的函数为

OS_EventTaskRdy,参数为事件ecb的指针

该函数实现的功能为调用这个函数的任务在任务等待ecb中清零,然后将任务就绪表中对应位置写1,标识任务已经就绪,该函数主要在事件发送过程中调用,OSXXXPOST()函数调用.代码如下

y    = OSUnMapTbl[pevent->OSEventGrp];

x    = OSUnMapTbl[pevent->OSEventTbl[y]];

prio = (INT8U)((y << 3u) + x);

ptcb                  =  OSTCBPrioTbl[prio];

ptcb->OSTCBDly        =  0u;

if ((ptcb->OSTCBStat &   OS_STAT_SUSPEND) == OS_STAT_RDY) {

OSRdyGrp         |=  ptcb->OSTCBBitY;

OSRdyTbl[y]      |=  ptcb->OSTCBBitX;

OS_EventTaskRemove(ptcb, pevent);

可以看到,获取ecb指针之后,从ecb等待任务表中获取最高优先级的等待任务的优先级,用优先级获取tcb,在任务未挂起的情况下,将系统就绪表中的就绪标志置一,从而标识系统就绪,

综合起来看,也就是说,任务请求消息的时候可能会让自身被挂起,而任务发送消息的时候,会让消息ecb中最高优先级的等待消息任务被激活.

OS_EventTaskRemove代码为

y                       =  ptcb->OSTCBY;

pevent->OSEventTbl[y]  &= (OS_PRIO)~ptcb->OSTCBBitX;

if (pevent->OSEventTbl[y] == 0u) {

pevent->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;

}

从任务ecb中将当前最高优先级的等待任务清除掉,因为该任务已经获得事件了

有时候任务是有等待时限的,当任务等待一个事件的时候,设置了等待时限,当等待时限到达,任务会继续执行,防止因为等待事件造成任务卡死,在OSXXXPend中有这样的代码

OSTCBCur->OSTCBStat     |= OS_STAT_MBOX;          /* Message not available, task will pend         */

OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;

OSTCBCur->OSTCBDly       = timeout;               /* Load timeout in TCB                           */

此处将OSTCBStat设置为OS_STAT_MBOX,并设置了一个等待时间timeout,那就必然和OSTimeTick有关系,查找OSTimeTick,发现这么一段

if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {

ptcb->OSTCBStat  &= (INT8U)~(INT8U)OS_STAT_PEND_ANY;

ptcb->OSTCBStatPend = OS_STAT_PEND_TO;

} else {

ptcb->OSTCBStatPend = OS_STAT_PEND_OK;

}

也就是说,timetick工作的时候在将OSTCBDly递减,当递减到0的时候,检测OSTCBStat,如果这是一个事件,那么ptcb->OSTCBStat  &= (INT8U)~(INT8U)OS_STAT_PEND_ANY;这句话能够让任务成为ready状态,同时OSTCBStatPend设置为OS_STAT_PEND_TO,是的任务能够在下面的调度中运行起来.

但是,这个请求并不是被删除了,而是依旧存在于任务ecb中,当系统post消息的时候,依旧会将消息给之前等待的任务.

事件的基本调度过程就是如上所说,下面将讲述如何具体的对各个信号量进行调用和调度.

uos事件控制块与任务同步的更多相关文章

  1. uCos-II中任务的同步与通信

    任务的同步与通信 任务间的同步 在多任务合作工作过程中,操作系统要解决两个问题: 各任务间应该具有一种互斥关系,即对某些共享资源,如果一个任务正在使用,则其他任务只能等待,等到该任务释放资源后,等待任 ...

  2. μC/OS-II 任务的同步与通信 --- 信号量

    任务间通信 系统中的多个任务在运行时,经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的限制和制约,才保证任务的顺利运行.因此,操作系统必须具有对任务的运行进行 ...

  3. RT-Thread--线程间同步

    线程间同步 一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递: 如果对共享内存的访问不是排他性的,那么各个线程 ...

  4. 鸿蒙内核源码分析(事件控制篇) | 任务间多对多的同步方案 | 百篇博客分析OpenHarmony源码 | v30.02

    百篇博客系列篇.本篇为: v30.xx 鸿蒙内核源码分析(事件控制篇) | 任务间多对多的同步方案 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当 ...

  5. 开源即时通讯GGTalk 8.0发布,增加Linux客户端,支持在统信UOS、银河麒麟上运行!

    GGTalk在2021年推出7.0后,经过一年多时间的开发,终于推出8.0版本,实现了Linux客户端. 这几年,信创国产化的势头越来越猛,政府事企业单位都在逐步转向使用国产OS.国产CPU.国产数据 ...

  6. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  7. FFmpeg学习6:视音频同步

    在上一篇文章中,视频和音频是各自独立播放的,并不同步.本文主要描述了如何以音频的播放时长为基准,将视频同步到音频上以实现视音频的同步播放的.主要有以下几个方面的内容 视音频同步的简单介绍 DTS 和 ...

  8. SQLServer事务同步下如何收缩日志

    事务同步是SQLServer做读写分离的一种常用的方式. 随着业务数据的不断增长,数据库积攒了大量的日志,为了腾出硬盘空间,需要对数据库日志进行清理 订阅数据库的日志清理 因为订阅数据库所有的数据都来 ...

  9. 多线程的通信和同步(Java并发编程的艺术--笔记)

    1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递.   2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...

随机推荐

  1. java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to com.bjsxt.service.UserServiceImpl01_AOP.

    对于Spring AOP 采用两种代理方法,一种是常规JDK,一种是CGLIB,我的UserDao了一个接口IUserDao,当代理对象实现了至少一个接口时,默认使用 JDK动态创建代理对象,当代理对 ...

  2. 4-20ma电流信号转0-5v()

    源:4-20ma电流信号转0-5v 电压转电流电路原理图(0-5V转4-20mA) 4mA-20mA转0-5v电路问题,LM324一直输出10V

  3. DLL、lib等链接库文件的使用

    由于遇见过多次动态链接库的使用,自己也写过DLL,每次都要费好大劲去配置,现在就简单的总结一下,争取以后少走弯路! 一般都会有三个文件: .h  头文件 .lib  静态链接库 .dll  动态链接库 ...

  4. regress_partition.sql

    --ENV --UAT @/test/change/env/env_test_uat.sql set echo on time on timing on set feedback on set pag ...

  5. android 以不规则图片为边框切割另外图片

    转自:http://blog.sina.com.cn/s/blog_474928c90101dkvf.html 最近工作上遇到了一个将一个图片按照相框进行裁剪的问题,花了一个下午研究了下,在此整理一下 ...

  6. 【转】SSL/TLS/WTLS协议原理

    1 SSL(Secure Socket Layer)是netscape公司设计的主要用于web的安全传输协议.这种协议在WEB上获得了广泛的应用.2 IETF(www.ietf.org )将SSL作了 ...

  7. Kubernetes 1.4 部署

    k8s 1.4 新版本部署 测试环境: node-: 10.6.0.140 node-: 10.6.0.187 node-: 10.6.0.188 kubernetes 集群,包含 master 节点 ...

  8. line-box(转)

    inline-block是什么? Inline-block是元素display属性的一个值.这个名字的由来是因为,display设置这个值的元素,兼具行内元素( inline elements)跟块级 ...

  9. Python之路: 面向对象

    Python是一门面向对象的语言,所以在Python中创建一个类和对象是很容易的.接下来了解一下面像对象的一些基本特征. 面向对象技术简介 类(class):描述具有相同的属性和方法的对象的集合.定义 ...

  10. LINQ to SQL语句对应SQL的实现

    LINQ to SQL语句(1)之Where LINQ to SQL语句(2)之Select/Distinct LINQ to SQL语句(3)之Count/Sum/Min/Max/Avg LINQ ...