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

队列是用在任务之间传送多个消息的时候,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. 破解MyEclipse2013注册码

    1.下载破解工具 http://down8.3987.com:801/2010/Myeclipse_zcj.3987.com.rar 2.打开 找到meclipse安装路径找到plugins文件夹打开 ...

  2. 设置控件全局显示样式 appearance

    iOS5及其以后提供了一个比较强大的工具UIAppearance,我们通过UIAppearance设置一些UI的全局效果,这样就可以很方便的实现UI的自定义效果又能最简单的实现统一界面风格,它提供如下 ...

  3. sql server 2005导出数据

    */ EXEC sp_configure 'show advanced options', 1 GO */     配置选项 'show advanced options' 已从 0 更改为 1.请运 ...

  4. mapreduce 依赖组合

    mport java.io.IOException;import java.util.StringTokenizer; import org.apache.hadoop.conf.Configurat ...

  5. 【啊哈!算法】算法6:只有五行的Floyd最短路算法

            暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程.         上图中有 ...

  6. jq的遍历节点

    1.child()方法 该方法用于取得匹配元素的子元素集合 2.next() 该方法用于取得匹配元素后面紧邻的同辈元素, 3.prev() 该方法用于取得匹配元素前面紧邻的同辈元素 4.sibling ...

  7. 剑指offer反转链表

    way1: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 3 ...

  8. JS 从一个字符串中截取两个字符串之间的字符串

    /************************************************* 函数说明:从一个字符串中截取 两个字符串之间的字符串 参数说明:src_str 原串, start ...

  9. 遍历Map key-value的两种方法

    以前遍历Map key-value比较习惯的方式是先获取Map中的所有key值,然后根据key,依次从Map中去数据,基本方式如下: Map<String,String> testData ...

  10. ARMs3c2440开发板挂接NFS服务

    1.修改IP地址,使虚拟机,电脑PC机.开发板位于同一个网段,开发板网线与电脑网络接口连接,如PC:ip 192.168.0.112 255.255.255.0 虚拟机 192.168.0.8 255 ...