ucos操作系统中的queue机制同样使用了event机制来实现,其实和前面的sem,mutex实现类似,所不同的是对sem而言,任务想获得信号量,对mutex而言,任务想获得的是互斥锁。任务间通信的queue机制则是想获得在queue中的消息,通过队列先进先出的形式存放消息。其实queue中存放的是放消息的内存的地址,通过读取地址可以获得消息的内容。

queue机制是有一段循环使用的内存来存放增加的消息,然后从这段内存中读取消息的一个过程。有专门的操作系统queue结构(OS_Q)来描述这段内存。系统中OS_Q的个数也是有限的,在创建queue时,每一个OS_Q和event是一一对应的。OS_Q的结构体代码如下所示:

typedef struct os_q {                   /* QUEUE CONTROL BLOCK                                         */
struct os_q *OSQPtr; /* Link to next queue control block in list of free blocks */
void **OSQStart; /* Pointer to start of queue data */
void **OSQEnd; /* Pointer to end of queue data */
void **OSQIn; /* Pointer to where next message will be inserted in the Q */
void **OSQOut; /* Pointer to where next message will be extracted from the Q */
INT16U OSQSize; /* Size of queue (maximum number of entries) */
INT16U OSQEntries; /* Current number of entries in the queue */
} OS_Q;

结构体中第1个参数是一个os_q指针,指向下一个空闲的os_q;第2和第3个参数是描述queue内存地址开始和结束的二维指针;第4和第5个参数是描述queue内存地址中消息放入和取出的地址的指针,第6个参数是这个queue的大小;第7个参数则是表示有多少个消息实体在这个queue里;这些中OSQStart,OSQEnd,OSQIn和OSQOut都是二维指针,其中放的都是一些信息的地址,这些信息可以在不同的地方创建,地址可以不连续。

使用语言描述一下这个结构体具体实现的是一个什么东东,OSQStart和OSQEnd唯一确定一个OSQSize大小的queue;OSQIn在创建queue之初的初始化过程中指向的位置是OSQStart,每次当有新的信息加入到queue中时,OSQIn会向OSQEnd方向地址加一操作,同时OSQEntries会加一操作,当OSQIn的地址值等于OSQEnd时,表示queue已经满了,需要从心开始OSQIn等于OSQStart;OSQOut同样也是指向OSQStart的位置,当有信息从queue中取出时,OSQOut同样会向OSQEnd方向加一操作,同时OSQEntries会减一操作。

从queue的创建开始看其具体的实现机制是如何的,queue的创建时通过OSQCreate函数实现的,代码如下:

OS_EVENT  *OSQCreate (void **start, INT16U size)
{
OS_EVENT *pevent;
OS_Q *pq; if (OSIntNesting > ) { /* See if called from ISR ... */
return ((OS_EVENT *)); /* ... can't CREATE from an ISR */
}
OS_ENTER_CRITICAL();
(1)==========================================================================================================
pevent = OSEventFreeList; /* Get next free event control block */
if (OSEventFreeList != (OS_EVENT *)) { /* See if pool of free ECB pool was empty */
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
(2)=========================================================================================================
if (pevent != (OS_EVENT *)) { /* See if we have an event control block */
OS_ENTER_CRITICAL();
pq = OSQFreeList; /* Get a free queue control block */
if (pq != (OS_Q *)) { /* Were we able to get a queue control block ? */
OSQFreeList = OSQFreeList->OSQPtr; /* Yes, Adjust free list pointer to next free*/
OS_EXIT_CRITICAL();
pq->OSQStart = start; /* Initialize the queue */
pq->OSQEnd = &start[size];
pq->OSQIn = start;
pq->OSQOut = start;
pq->OSQSize = size;
pq->OSQEntries = ;
pevent->OSEventType = OS_EVENT_TYPE_Q;
pevent->OSEventCnt = ;
pevent->OSEventPtr = pq;
OS_EventWaitListInit(pevent); /* Initalize the wait list */
} else {
pevent->OSEventPtr = (void *)OSEventFreeList; /* No, Return event control block on error */
OSEventFreeList = pevent;
OS_EXIT_CRITICAL();
pevent = (OS_EVENT *);
}
}
return (pevent);
(3)=======================================================================================================
}

可以从代码中看出,queue创建的开始部分和sem,mutex相同,就是不要在中断中创建,第二部分也相同就是从空闲event中取一个空闲的event结构体。不同的是第三部分,sem,mutex没有专门的描述机制相关的结构体,所以说不需要对其进行初始化,只要对event结构初始化就可以,但是对于queue来说除了初始化event之外,还需要初始化queue结构,上面的代码已经很清晰的做了相关操作,结合struct os_q的参数介绍,可以很好的理解,最后queue的地址放在event的OSEventPtr指针中。

void  *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)
{
void *pmsg;
OS_Q *pq; if (pevent->OSEventType != OS_EVENT_TYPE_Q) {/* Validate event block type */
*perr = OS_ERR_EVENT_TYPE;
return ((void *));
}
if (OSIntNesting > ) { /* See if called from ISR ... */
*perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */
return ((void *));
}
if (OSLockNesting > ) { /* See if called with scheduler locked ... */
*perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */
return ((void *));
}
OS_ENTER_CRITICAL();
(1)====================================================================================================
pq = (OS_Q *)pevent->OSEventPtr; /* Point at queue control block */
if (pq->OSQEntries > ) { /* See if any messages in the queue */
pmsg = *pq->OSQOut++; /* Yes, extract oldest message from the queue */
pq->OSQEntries--; /* Update the number of entries in the queue */
if (pq->OSQOut == pq->OSQEnd) { /* Wrap OUT pointer if we are at the end of the queue */
pq->OSQOut = pq->OSQStart;
}
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
return (pmsg); /* Return message received */
}
(2)=====================================================================================================
OSTCBCur->OSTCBStat |= OS_STAT_Q; /* Task will have to pend for a message to be posted */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout; /* Load timeout into TCB */
OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next highest priority task ready to run */
OS_ENTER_CRITICAL();
switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */
case OS_STAT_PEND_OK: /* Extract message from TCB (Put there by QPost) */
pmsg = OSTCBCur->OSTCBMsg;
*perr = OS_ERR_NONE;
break; case OS_STAT_PEND_ABORT:
pmsg = (void *);
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */
break; case OS_STAT_PEND_TO:
default:
OS_EventTaskRemove(OSTCBCur, pevent);
pmsg = (void *);
*perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event within TO */
break;
}+
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *); /* Clear event pointers */
OSTCBCur->OSTCBMsg = (void *); /* Clear received message */
OS_EXIT_CRITICAL();
return (pmsg); /* Return received message */
(3)========================================================================================================
}

queue队列的pend函数是OSQPend,从函数的代码看,其与sem主要的不同在第二部分,而与mutex的不同主要是在第二和第三部分(因为mutex有优先级的继承操作)。pend操作的第二部分主要的实现是从event的OSEventPtr中取出queue的描述结构体os_q,然后通过OSQEntries是否大于0判断在当前的queue中是否有信息,如果有则从OSQOut中取出信息,并将OSQOut加操作,让其指向下一个信息地址,同时将queue中代表信息个数的OSQEntries剪操作;如果OSQOut已经达到queue的最后一个位置即OSQOut==OSQEnd,则循环开始从OSQOut=OSQStart,重新获取信息;如果queue队列中没有信息,则直接将当前调度pend的任务挂起,并且重新进行任务调度,当任务重新获得运行的时候表示已经获得了event中的queue信息,则重新将当前任务加到运行等待列表中,这是第三部分的内容。

INT8U  OSQPost (OS_EVENT *pevent, void *pmsg)
{
OS_Q *pq; if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */
return (OS_ERR_EVENT_TYPE);
}
OS_ENTER_CRITICAL();
(1)====================================================================================================
if (pevent->OSEventGrp != ) { /* See if any task pending on queue */
/* Ready highest priority task waiting on event */
(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_Q, OS_STAT_PEND_OK);
OS_EXIT_CRITICAL();
OS_Sched(); /* Find highest priority task ready to run */
return (OS_ERR_NONE);
}
(2)====================================================================================================
pq = (OS_Q *)pevent->OSEventPtr; /* Point to queue control block */
if (pq->OSQEntries >= pq->OSQSize) { /* Make sure queue is not full */
OS_EXIT_CRITICAL();
return (OS_ERR_Q_FULL);
}
*pq->OSQIn++ = pmsg; /* Insert message into queue */
pq->OSQEntries++; /* Update the nbr of entries in the queue */
if (pq->OSQIn == pq->OSQEnd) { /* Wrap IN ptr if we are at end of queue */
pq->OSQIn = pq->OSQStart;
}
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
(3)====================================================================================================
}

queue的post函数是OSQPost,该函数的入参是event指针,和要加入queue的信息的地址pmsg,和sem以及mutex类似,queue首先判断的是event中是否有在等待queue信息的任务,如果有的话将该pmsg直接交给等待任务,从代码中可以找到任务TCB的结构体中有一个专门存放pmsg的变量OSTCBMsg,这样的话,任务会通过访问OSTCBMsg直接获得pmsg的信息;如果在event中没有等待信息的任务存在则会进入到第三部分,就是从event中取出描述queue的os_q结构体,然后判断当前的queue中存在的信息是否达到了queue的上限OSQSize,如果已经达到上限,则会返回queue满了的错误,如果没有的话,会将pmsg放到OSQIn中,并且将queue中表示信息个数的OSQEntries加操作,如果OSQIn已经达到queue的上限,则会循环从Queue开始的地方存放pmsg (OSQIn == OSQStart),其实OSQIn的范围和OSQOut的范围是一样的,OSQIn访问的内存地址,OSQOut必然会访问到。 在queue中如果有msg的存在的话,在event任务等待列表中就不会有任务在等待msg,这是和其他的机制相通的地方。

ucos实时操作系统学习笔记——任务间通信(队列)的更多相关文章

  1. ucos实时操作系统学习笔记——任务间通信(消息)

    ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...

  2. ucos实时操作系统学习笔记——任务间通信(信号量)

    ucos实时操作系统的任务间通信有好多种,本人主要学习了sem, mutex, queue, messagebox这四种.系统内核代码中,这几种任务间通信机制的实现机制相似,接下来记录一下本人对核心代 ...

  3. ucos实时操作系统学习笔记——任务间通信(互斥锁)

    想讲一下ucos任务间通信中的mutex,感觉其设计挺巧妙,同sem一样使用的是event机制实现的,代码不每一行都分析,因为讲的没邵贝贝老师清楚,主要讲一下mutex的内核是如何实现的.可以理解互斥 ...

  4. ucos实时操作系统学习笔记——操作系统在STM32的移植

    使用ucos实时操作系统是在上学的时候,导师科研项目中.那时候就是网上找到操作系统移植教程以及应用教程依葫芦画瓢,功能实现也就罢了,没有很深入的去研究过这个东西.后来工作了,闲来无聊就研究了一下这个只 ...

  5. ucos实时操作系统学习笔记——内核结构和任务创建

    对于ucos实时操作系统,邵贝贝的那本书已经写得很详细了,我因为之前不深的研究过ucos,所以在这里做一个笔记,写一些个人对该操作系统的理解,仅仅是个人理解,如果有人看到这边随笔有不对的地方,望给我指 ...

  6. RTX51 Tiny实时操作系统学习笔记—初识RTX51 Tiny

     一,RTX51 Tiny简单介绍    RTX51 Tiny是一种实时操作系统(RTOS),能够用它来建立多个任务(函数)同一时候运行的应用(从宏观上看是同一时候运行的,但从微观上看,还是独立运行的 ...

  7. 操作系统学习笔记----进程/线程模型----Coursera课程笔记

    操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...

  8. 操作系统学习笔记5 | 用户级线程 && 内核级线程

    在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...

  9. 深挖计算机基础:趣谈Linux操作系统学习笔记

    参考极客时间专栏<趣谈Linux操作系统>学习笔记 核心原理篇:内存管理 趣谈Linux操作系统学习笔记:第二十讲 趣谈Linux操作系统学习笔记:第二十一讲 趣谈Linux操作系统学习笔 ...

随机推荐

  1. ASP.NET MVC 请求路径相关参数的获取

    Request.ApplicationPath / Request.CurrentExecutionFilePath /Home/Index Request.FilePath /Home/Index ...

  2. 数据库优化案例——————某市中心医院HIS系统

    记得在自己学习数据库知识的时候特别喜欢看案例,因为优化的手段是容易掌握的,但是整体的优化思想是很难学会的.这也是为什么自己特别喜欢看案例,今天也开始分享自己做的优化案例. 最近一直很忙,博客产出也少的 ...

  3. OVS local network 连通性分析 - 每天5分钟玩转 OpenStack(132)

    前面已经创建了两个 OVS local network,今天详细分析它们之间的连通性. launch 新的 instance "cirros-vm3",网络选择 second_lo ...

  4. C++中的引用

    一,C++中引用的基础知识 1.引用的基本概念 1.所谓的引用其实就是对变量起“别名”.引用和变量对应得是相同的内存,修改引用的值,变量的值也会改变,和指针类似. 2.引用在定义的时候必须要初始化,初 ...

  5. iOS审核这些坑,腾讯游戏也踩过

    作者:Jamie,专项技术测试工程师,在iOS预审和ASO优化领域从事专项测试相关工作,为腾讯游戏近100个产品提供专项服务. WeTest 导读 在App上架苹果应用商店的过程中,相信大多数iOS开 ...

  6. [原] KVM 虚拟化原理探究(3)— CPU 虚拟化

    KVM 虚拟化原理探究(3)- CPU 虚拟化 标签(空格分隔): KVM [TOC] CPU 虚拟化简介 上一篇文章笼统的介绍了一个虚拟机的诞生过程,从demo中也可以看到,运行一个虚拟机再也不需要 ...

  7. 在 SharePoint Server 2016 本地环境中设置 OneDrive for Business

    建议补丁 建议在sharepoint2016打上KB3127940补丁,补丁下载地址 https://support.microsoft.com/zh-cn/kb/3127940 当然不打,也可以用O ...

  8. iOS中支付宝集成

    iOS中支付宝集成 如今各种的App中都使用了三方支付的功能,现在将我在使用支付宝支付集成过程的心得分享一下,希望对大家都能有所帮助 要集成一个支付宝支付过程的环境,大致需要: 1>公司:先与支 ...

  9. Linux实战教学笔记02:计算机系统硬件核心知识

    标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 互联网企业常见服务器介绍 1.1 互联网公司服务器品牌 - DELL(大多数公司,常用) - HP - IBM(百度在用) 浪潮 联想 航天联 ...

  10. 【swift】BlockOperation和GCD实用代码块

    //BlockOperation // // ViewController.swift import UIKit class ViewController: UIViewController { @I ...