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
ucos队列的实现--源码分析的更多相关文章
- 延迟队列DelayQueue take() 源码分析
延迟队列DelayQueue take() 源码分析 在工作中使用了延迟队列,对其内部的实现很好奇,于是就研究了一下其运行原理,在这里就介绍一下take()方法的源码 1 take()源码 如下所示 ...
- java并发包——阻塞队列BlockingQueue及源码分析
一.摘要 BlockingQueue通常用于一个线程在生产对象,而另外一个线程在消费这些对象的场景,例如在线程池中,当运行的线程数目大于核心的线程数目时候,经常就会把新来的线程对象放到Blocking ...
- 【RabbitMQ学习记录】- 消息队列存储机制源码分析
本文来自 网易云社区 . RabbitMQ在金融系统,OpenStack内部组件通信和通信领域应用广泛,它部署简单,管理界面内容丰富使用十分方便.笔者最近在研究RabbitMQ部署运维和代码架构,本篇 ...
- JAVA并发(7)-并发队列PriorityBlockingQueue的源码分析
本文讲PriorityBlockingQueue(优先阻塞队列) 1. 介绍 一个无界的具有优先级的阻塞队列,使用跟PriorityQueue相同的顺序规则,默认顺序是自然顺序(从小到大).若传入的对 ...
- 消息队列中间件 RocketMQ 源码分析 —— Message 存储
- 阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- jQuery.queue源码分析
作者:禅楼望月(http://www.cnblogs.com/yaoyinglong ) 队列是一种特殊的线性表,它的特殊之处在于他只允许在头部进行删除,在尾部进行插入.常用来表示先进先出的操作(FI ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
随机推荐
- CodeForces 687A NP-Hard Problem(二分图判定)
这本来一个挺简单的题呢,结果让我给想复杂了,二分图就是把图分成了两部分,然后不同颜色各一边,肯定是满足题目中说的边和点的条件的,真是犯二了.. 代码如下: #include<iostream&g ...
- 转:Windows平台配置Appium+Java环境
1) 安装JDK 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 安装 ...
- 如何获取url访问历史记录
在院里的群里,有人问了这么一个问题: A页面提交表单到B页面,然后在B页面点了后退,如果在A页面上判断是直接访问的还是后退进去的呢?我不想改B页面. 于是乎本着热心人的想法,我就帮他搞了搞,首先我想到 ...
- C++设计模式-单件
理解 1. Singleton模式用来取代全局静态变量.C++通过静态成员变量来实现类实例全局唯一性. 2. instance()方法是单件提供的唯一调用入口. 要点 1. ...
- ext3文件系统目录限制问题
昨晚排查了在KVM的build系统中的一个问题,跟踪到后面发现在一个目录下mkdir创建目录失败.我手动试了一下,提示如下:cannot create directory `/home/master/ ...
- 浅谈Android五大布局
Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦.组件按照布局的要求依次排列,就组成了用户所看见的界面.Android的五大布局分别是LinearLay ...
- Super Mario
Super Mario Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit ...
- JAVA实现二进制,十六进制输出
public class Main { /** * @param args */ public static void main(String[] args) { // TODO Auto-gener ...
- opcode修改
Smali: if-eqz opcode 38 if-nez opcode 39 SO: CMP R0,#0 00 28 CMP R0,#1 01 28
- 使用virsh命令创建KVM虚拟机快照
查看虚拟机所在主机和虚拟机名称:[root@node-1 ~]# nova show a88dcf5d-c8b2-46a5-af27-a176d8235c9d|grep hyper| OS-EXT-S ...