之前说到事件,讲了事件,信号量和互斥信号量,还有一个队列没说,今天说说队列.

队列是用在任务之间传送多个消息的时候,a任务发送消息,b任务发送消息,然后c任务可以依次去提取出b和a传递的消息,不会造成系统的阻塞,他的实现结构如下

在队列的实现中,也是使用事件ecb,OSEventType为OS_EVENT_TYPE_Q类型,而其OSEventPtr指向一个QS_Q结构的指针,该结构的定义如下

typedef struct os_q {                   /* QUEUE CONTROL BLOCK                                         */

struct os_q   *OSQPtr;              /* 指向下一个队列控制块 */

void         **OSQStart;            /* 指向消息指针数组的起始地址*/

void         **OSQEnd;              /* 指向消息指针数组的下一个单元,构成循环数组缓冲区 */

void         **OSQIn;               /*指向插入消息的位置,其与OSQEnd相等时,调整到指向数组的起始单元*/

void         **OSQOut;              /*指向被取出消息的位置,其与OSQEnd相等时,调整到指向数组的起始单元 */

INT16U         OSQSize;             /*数组的长度*/

INT16U         OSQEntries;          /*以存放消息指针的元素数目*/

} OS_Q;

我们可以称呼这种结构叫做QCB,队列控制快.在系统初始化的时候会有很多个队列控制快,系统初始化的时候将队列控制快连接成一个链表,如下

OS_EXT  OS_Q              OSQTbl[OS_MAX_QS];

队列控制快数量为OS_MAX_QS,该参数在os_cfg.h文件中指定

#define OS_MAX_QS                 5u

系统存放的空闲队列控制块链表指针为

OS_EXT  OS_Q             *OSQFreeList;

在系统初始化的时候os_init函数中调用OS_QInit函数,该函数中有如下代码

for (ix = 0u; ix < (OS_MAX_QS - 1u); ix++) {     /* Init. list of free QUEUE control blocks        */

ix_next = ix + 1u;

pq1 = &OSQTbl[ix];

pq2 = &OSQTbl[ix_next];

pq1->OSQPtr = pq2;

}

pq1         = &OSQTbl[ix];

pq1->OSQPtr = (OS_Q *)0;

OSQFreeList = &OSQTbl[0];

可以看到,这一段类似于tcb和ecb的初始化,将OSQTbl结构连接成链表,链表头为OSQFreeList,之后,当创建队列的时候从空闲链表中取出链表头作为队列事件,如下

if (pq != (OS_Q *)0) {

OSQFreeList            = OSQFreeList->OSQPtr;

OS_EXIT_CRITICAL();

pq->OSQStart           = start;

pq->OSQEnd             = &start[size];

pq->OSQIn              = start;

pq->OSQOut             = start;

pq->OSQSize            = size;

pq->OSQEntries         = 0u;

pevent->OSEventType    = OS_EVENT_TYPE_Q;

pevent->OSEventCnt     = 0u;

pevent->OSEventPtr     = pq;

可以看到第二句话里面就是讲空闲队列控制块链表的链表头后移动到下一个元素,然后将start元素进行赋值,这个start元素师创建的时候给定的,参数如下

OS_EVENT  *OSQCreate (void    **start,INT16U    size)

第一个参数为一个指针数组,第二个参数为指针数组大小,也就是说,start指向一个指针数组的第一个元素地址,而数组里面存放的是指向具体的消息的指针,我们可以形象的说,start相当于一个messagetable的首地址.

系统发送消息队列的时候使用函数OSQPost函数,其中核心代码为

*pq->OSQIn++ = pmsg;

pq->OSQEntries++;

if (pq->OSQIn == pq->OSQEnd) {

pq->OSQIn = pq->OSQStart;

这段代码向列表中添加一个元素,然后当添加的元素超过end的时候循环到头上,一般来说,队列取出元素会分为先进先出和后进先出两种,我们先来看看申请队列的函数就能明白ucos采用了什么方式,申请队列的函数为OSQPend函数

if (pq->OSQEntries > 0u) {

pmsg = *pq->OSQOut++;

pq->OSQEntries--;

if (pq->OSQOut == pq->OSQEnd) {

pq->OSQOut = pq->OSQStart;

}

这段代码需要和上面两段结合起来看,初始化的时候OSQIn和OSQOut都指向start头,插入的时候OSQIn++,取出的时候OSQOut++,当插入第三个元素之后, OSQIn指向了start+3,但是OSQOut依然是指向start的,取出元素的时候取出的是最早添加到队列中的元素,因此可以认为,系统的队列维护方式是先进先出, OSQEntries标识系统中该队列含有的信号数量,只有当数量不为0的时候,才能从队列中取出元素

另外,为了系统灵活性,ucos提供了函数可以发送消息到队列开头,函数为OSQPostFront,实现如下

pq->OSQOut--;

*pq->OSQOut = pmsg;

pq->OSQEntries++;

可以看到,这个函数的实现和OSQPost的实现方式并不同, OSQPost的实现依靠的是OSQIn,而OSQPostFront依靠的是OSQOut,原因仔细想想也很简单,因为OSQIn和OSQOut是相关联的,我们来打个比方,如果此时OSQOut和OSQIn都指向start+1的位置,此时OSQPost三个元素,那么OSQIn为start+4,但是OSQOut依然是start+1,这个时候我们要在队列的头部插入一个元素,那么选择有两种,第一个是将之前插入的元素每一个都往后移动,使得OSQOut指向刚刚插入的元素,这样需要循环移植3个元素,时间不可测,犯了操作系统的大忌,所以不可取,不如直接将元素加入队列, OSQOut往前移动一位,此时OSQOut为start,到OSQIn距离为四个元素,很好的实现了目标

但是,实现这个的前提是我们实现了循环队列,而信号队列正好是循环队列.

说到这里队列的原理基本讲清楚了,接下来总结一下

创建消息队列

QSQCreate

请求消息队列

OSQPend

向消息队列发送消息

OSQPost

发送消息到队列头部

OSQPostFront

清空消息队列

OSQFlush

删除消息队列

OSQDel

查询消息队列

OSQQuery

消息队列使用了消息队列控制块qcb,队列控制块构成空闲链表,创建队列每次从空闲链表中去取出一个元素,消息控制块内部拥有一个循环链表,over

ucos队列的实现--源码分析的更多相关文章

  1. 延迟队列DelayQueue take() 源码分析

    延迟队列DelayQueue take() 源码分析 在工作中使用了延迟队列,对其内部的实现很好奇,于是就研究了一下其运行原理,在这里就介绍一下take()方法的源码 1 take()源码 如下所示 ...

  2. java并发包——阻塞队列BlockingQueue及源码分析

    一.摘要 BlockingQueue通常用于一个线程在生产对象,而另外一个线程在消费这些对象的场景,例如在线程池中,当运行的线程数目大于核心的线程数目时候,经常就会把新来的线程对象放到Blocking ...

  3. 【RabbitMQ学习记录】- 消息队列存储机制源码分析

    本文来自 网易云社区 . RabbitMQ在金融系统,OpenStack内部组件通信和通信领域应用广泛,它部署简单,管理界面内容丰富使用十分方便.笔者最近在研究RabbitMQ部署运维和代码架构,本篇 ...

  4. JAVA并发(7)-并发队列PriorityBlockingQueue的源码分析

    本文讲PriorityBlockingQueue(优先阻塞队列) 1. 介绍 一个无界的具有优先级的阻塞队列,使用跟PriorityQueue相同的顺序规则,默认顺序是自然顺序(从小到大).若传入的对 ...

  5. 消息队列中间件 RocketMQ 源码分析 —— Message 存储

  6. 阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)

  7. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  8. jQuery.queue源码分析

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong ) 队列是一种特殊的线性表,它的特殊之处在于他只允许在头部进行删除,在尾部进行插入.常用来表示先进先出的操作(FI ...

  9. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

随机推荐

  1. JavaScript(1)——变量、函数声明及作用域

    这是我的第一篇博客文章,本人不才,文笔也不好,所以可能写的有点凌乱.有什么不对的地方还望见谅.不过每天进步一小步,总有一天会迈出那一大步.以下内容是我对变量.函数声明及函数表达式.作用域的理解. [变 ...

  2. CC_CALLBACK之间的区别

    #define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_A ...

  3. hdu - 2586 How far away ?(最短路共同祖先问题)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 最近公共祖先问题~~LAC离散算法 题目大意:一个村子里有n个房子,这n个房子用n-1条路连接起 ...

  4. ubuntu11.10server 安装redis-2.6.7

    1.下载安装: 1 2 3 4 5 6 cd /tmp wget http://redis.googlecode.com/files/redis-2.6.7.tar.gz tar -zxf redis ...

  5. 转:使用WebDriver过程中遇到的那些问题

    在做web项目的自动化端到端测试时主要使用的是Selenium WebDriver来驱动浏览器.Selenium WebDriver的优点是支持的语言多,支持的浏览器多.主流的浏览器Chrome.Fi ...

  6. js插入节点appendChild和insertBefore

    首先 从定义来理解 这两个方法: appendChild() 方法:可向节点的子节点列表的末尾添加新的子节点.语法:appendChild(newchild) insertBefore() 方法:可在 ...

  7. MQ日常维护操作手册

    假设队列管理器为QMgrName,以下所有使用QMgrName的地方您都可以替换成您维护的mq队列管理器名称. 一.MQ的启动与停止 用root用户启/停需要root用户包含在mqm组中. 1.MQ的 ...

  8. Struts2语法--Ognl

    OGNL: Object Graph Navigation Language index.jsp: <body> 访问属性 <a href="<%=contextPa ...

  9. PL/SQL DEVELOPER 导出表数据

    http://jingyan.baidu.com/album/fcb5aff78e6a48edab4a7146.html?picindex=4 1. 导出表数据 打开pl/sql客户端 在左侧 点击t ...

  10. FusionCharts使用问题及解决方法(五)-FusionCharts常见问题大全

    在前4篇文章中,我们总结了FusionCharts XT图表使用中的一些常见问题(FAQ)及解决方法,本文继续讨论FusionCharts使用者常见的一些复杂报错及错误的调试/解决方法. 问题描述:是 ...