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事件控制块与任务同步的更多相关文章
-
uCos-II中任务的同步与通信
任务的同步与通信 任务间的同步 在多任务合作工作过程中,操作系统要解决两个问题: 各任务间应该具有一种互斥关系,即对某些共享资源,如果一个任务正在使用,则其他任务只能等待,等到该任务释放资源后,等待任 ...
-
μC/OS-II 任务的同步与通信 --- 信号量
任务间通信 系统中的多个任务在运行时,经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的限制和制约,才保证任务的顺利运行.因此,操作系统必须具有对任务的运行进行 ...
-
RT-Thread--线程间同步
线程间同步 一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递: 如果对共享内存的访问不是排他性的,那么各个线程 ...
-
鸿蒙内核源码分析(事件控制篇) | 任务间多对多的同步方案 | 百篇博客分析OpenHarmony源码 | v30.02
百篇博客系列篇.本篇为: v30.xx 鸿蒙内核源码分析(事件控制篇) | 任务间多对多的同步方案 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当 ...
-
[ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
-
FFmpeg学习6:视音频同步
在上一篇文章中,视频和音频是各自独立播放的,并不同步.本文主要描述了如何以音频的播放时长为基准,将视频同步到音频上以实现视音频的同步播放的.主要有以下几个方面的内容 视音频同步的简单介绍 DTS 和 ...
-
SQLServer事务同步下如何收缩日志
事务同步是SQLServer做读写分离的一种常用的方式. 随着业务数据的不断增长,数据库积攒了大量的日志,为了腾出硬盘空间,需要对数据库日志进行清理 订阅数据库的日志清理 因为订阅数据库所有的数据都来 ...
-
多线程的通信和同步(Java并发编程的艺术--笔记)
1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递. 2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...
-
.NET Core采用的全新配置系统[10]: 配置的同步机制是如何实现的?
配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置:第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置.要了解配置同步机制的实现原理,先得从认识一个 ...
随机推荐
-
认识DOM和一些方法
认识DOM 文档对象模型DOM(Document Object Model)定义访问和处理HTML文档的标准方法.DOM 将HTML文档呈现为带有元素.属性和文本的树结构(节点树). 先来看看下面代码 ...
-
Stanford机器学习---第三讲. 逻辑回归和过拟合问题的解决 logistic Regression &; Regularization
原文:http://blog.csdn.net/abcjennifer/article/details/7716281 本栏目(Machine learning)包括单参数的线性回归.多参数的线性回归 ...
-
Java [Leetcode 225]Implement Stack using Queues
题目描述: Implement the following operations of a stack using queues. push(x) -- Push element x onto sta ...
-
根据输出设置select的被选中值
$("#startupStatus").find("option").map(function(i) { if ($('#st-status').val() = ...
-
Log4Net 使用总结
在项目中要记录日志,便于程序调试.于是就想到了大名鼎鼎的Log4Net,这货可以方便地将日志信息记录到文件.控制台.Windows事件日志和数据库(包括MS SQL Server, Access, O ...
-
TDD(测试驱动开发)学习一:初识TDD
首先说一下名词解释,TDD,英文名称Test-Driven Development,中文名称测试驱动开发,简单的断下句“测试/驱动/开发”,简单的理解一下,就是测试驱动着开发,大白话就是说用一边测试一 ...
-
【朝花夕拾】Android性能篇之(二)Java内存分配
前言 在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给了JVM来处理了,而不需要手动在代码中去完成.有了虚拟机内存管理机制,也就不 ...
-
第五讲 DOM基础
DOM基础: 什么是DOM:其实就是dovument,div获取.修改样式等等,但是不只是js的组成部分,而且还是一套规范,规定了这些浏览器怎么处理这些操作: 浏览器支持情况:IE(IE7-8,10% ...
-
PHP后台登录 接口
/** * 登录 * tel 手机号 */ public function login(){ $param=input('param.'); if(!empty($param['tel'])){ if ...
-
【javascript基础】 JavaScript defer和async区别
defer该属性用来通知浏览器,这段脚本代码将不会产生任何文档内容.例如 JavaScript代码中的document.write()方法将不会骑作用,浏览器遇到这样的代码将会忽略,并继续执行后面的代 ...