ucos实时操作系统学习笔记——任务间通信(队列)

时间:2023-01-23 20:55:29

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. 深挖计算机基础:趣谈Linux操作系统学习笔记

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

  9. Vue – 基础学习&lpar;2&rpar;&colon;组件间 通信及参数传递

    Vue – 基础学习(2):组件间 通信及参数传递

随机推荐

  1. Codeforces Round &num;257 &lpar;Div&period; 2&rpar; A题

    A. Jzzhu and Children time limit per test 1 second memory limit per test 256 megabytes input standar ...

  2. lambda表達式

    lambda简介 lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他,“转到”或者 “成为”.运算符将表达式分为两部分,左边指定 ...

  3. 1&period; mybatis批量插入数据

    通过list <insert id="saveByList" useGeneratedKeys="true" parameterType="ja ...

  4. 转&colon; seajs知识点与cmd规范

    在比较之前,我们得先来了解下什么是AMD规范?什么是CMD规范?当然先申明一下,我个人也是总结下而已,也是网上看到的资料,自己总结下或者可以说整理下而已,供大家更深入的了解!因为我们都知道 AMD规范 ...

  5. ssh proxy配置

    在.ssh目录下新建config文件,并添加以下内容: Host 10.100.101.123 ProxyCommand=nc -X 5 -x [proxyip:proxyport] %h %p

  6. 《C&num;并发编程经典实例》学习笔记-第一章并发编程概述

    并发编程的术语 并发 同时做多件事情 多线程 并发的一种形式,它采用多个线程来执行程序. 多线程是并发的一种形式,但不是唯一的形式. 并行处理 把正在执行的大量的任务分割成小块,分配给多个同时运行的线 ...

  7. 傅立叶变换系列(五)快速傅立叶变换(FFT)

    说明: 傅里叶级数.傅里叶变换.离散傅里叶变换.短时傅里叶变换...这些理解和应用都非常难,网上的文章有两个极端:“Esay”  Or  “Boring”!如果单独看一两篇文章就弄懂傅里叶,那说明你真 ...

  8. git 推送出现 &quot&semi;fatal&colon; The remote end hung up unexpectedly&quot&semi;

    原因:原因是推送的文件太大 解决方案: 注意,有时候会看不到.git文件,可能被隐藏了,在这里勾选上隐藏的项目,就可以看到了. 第一种,全局设置 在C:\Users\wang\git\.git\con ...

  9. Spark2 SQL configuration参数配置

    查看当前环境SQL参数的配置 spark.sql("SET -v") key value spark.sql.hive.version 1.2.1 spark.sql.source ...

  10. svm核函数的理解和选择

    https://blog.csdn.net/leonis_v/article/details/50688766 特征空间的隐式映射:核函数    咱们首先给出核函数的来头:在上文中,我们已经了解到了S ...