ucos队列的实现--源码分析

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

队列是用在任务之间传送多个消息的时候,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

时间: 2024-10-25 13:27:47

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

JUC阻塞队列之DelayQueue源码分析

DelayQueue是一个支持延时获取元素的***阻塞队列.并且队列中的元素必须实现Delayed接口.在创建元素时可以指定多久才能从队列中获取当前元素.只有在延迟期满时才能从队列中获取到元素.DelayQueue的应用范围非常广阔,如可以用它来保存缓存中元素的有效期,也可用它来实现定时任务. Delayed接口 在分析DelayQueue源码之前,我们先来看看Delayd接口,其源码定义如下: public interface Delayed extends Comparable < Dela

jQuery.queue源码分析

作者:禅楼望月(http://www.cnblogs.com/yaoyinglong ) 队列是一种特殊的线性表,它的特殊之处在于他只允许在头部进行删除,在尾部进行插入.常用来表示先进先出的操作(FIFO)--先进队列的元素先出队.搜索整个jQuery库会发现,queue在jQuery内部仅供给animate动画来使用.它提供了对外的接口,因此程序员也可以使用队列来完成一些特殊需求. queue模块对外开放的API:工具方法:queue,dequeue,_queueHooks(仅内部使用)实例方

Android异步消息处理机制详解及源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 最近相对来说比较闲,加上养病,所以没事干就撸些自己之前的知识点为博客,方便自己也方便别人. 1 背景 之所以选择这个知识点来分析有以下几个原因: 逛GitHub时发现关注的isuss中有人不停的在讨论Android中的Looper , Handler , Me

[转]jQuery源码分析系列

文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaron/jQuery 正在编写的书 - jQuery架构设计与实现 本人在慕课网的教程(完结) jQuery源码解析(架构与依赖模块) 64课时 jQuery源码解析(DOM与核心模块)64课时 jQuery源码分析目录(完结) jQuery源码分析系列(01) : 整体架构 jQuery源码分析系列(

jQuery源码分析系列(38) : 队列操作

Queue队列,如同data数据缓存与Deferred异步模型一样,都是jQuery库的内部实现的基础设施 Queue队列是animate动画依赖的基础设施,整个jQuery中队列仅供给动画使用 Queue队列 队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除. 为什么要引入队列? 我们知道代码的执行流有异步与同步之分,例如 var a

jQuery源码分析系列(39) : 动画队列

data函数在jQuery中只有短短的300行代码,非常不起点 ,剖析源码的时候你会发现jQuery只要在有需要保存数据的地方无时无刻不依赖这个基础设施 动画会调用队列,队列会调用data数据接口还保存队列里面的的动画数据 所以我们在自习回顾下关于数据缓存 //These may be used throughout the jQuery core codebase //存数据的 //用户使用 data_user = new Data(); //存储对象 //jQuery内部私有 //用来存事件

Netty源码分析第7章(编码器和写数据)----&gt;第3节: 写buffer队列

Netty源码分析七章: 编码器和写数据 第三节: 写buffer队列 之前的小结我们介绍过, writeAndFlush方法其实最终会调用write和flush方法 write方法最终会传递到head节点, 调用HeadContext的write方法: public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { unsafe.write(msg, prom

Netty源码分析第7章(编码器和写数据)----&gt;第4节: 刷新buffer队列

Netty源码分析第七章: 编码器和写数据 第四节: 刷新buffer队列 上一小节学习了writeAndFlush的write方法, 这一小节我们剖析flush方法 通过前面的学习我们知道, flush方法通过事件传递, 最终会传递到HeadContext的flush方法: public void flush(ChannelHandlerContext ctx) throws Exception { unsafe.flush(); } 这里最终会调用AbstractUnsafe的flush方法

JUC源码分析-集合篇(五)BlockingQueue 阻塞式队列实现原理

JUC源码分析-集合篇(五)BlockingQueue 阻塞式队列实现原理 以 LinkedBlockingQueue 分析 BlockingQueue 阻塞式队列的实现原理. 1. 数据结构 LinkedBlockingQueue 和 ConcurrentLinkedQueue 一样都是由 head 节点和 last 节点组成,每个节点(Node)由节点元素(item)和指向下一个节点(next)的引用组成,节点与节点之间就是通过这个 next 关联起来,从而组成一张链表结构的队列.默认情况下