这节总结操作系统UCOSIII的内核对象------->消息队列
消息队列的作用:
是用来在任务与任务之间的数据传递,只不过传递的是数据的指针,不是数据的值
那么就是可以传递值的地址、一个函数地址、字符串首地址这三种
问题 什么是消息队列?
消息队列是一个结构体其类型就是OS_Q
typedef struct os_q OS_Q;
展开看看结构体成员**
struct os_q { /* Message Queue /
OS_OBJ_TYPE Type; / 记录内核的类型的变量 /
CPU_CHAR NamePtr; / 记录消息队列名字的指针/
OS_PEND_LIST PendList; /* 等待列表结构体变量 /
OS_MSG_Q MsgQ; / 消息列表结构体变量 */
};
下面简要说明消息队列结构体的重要成员的作用
PendList 等待列表 的功能根据信号量那一节已经介绍非常清楚了,那一节等待列表是用来当有任务等待信号量,但是发现没有信号量可以获取,只能将任务挂载在等待列表然后等待信号量到来就可以从等待列表中移出该任务,然后继续执行任务函数,本节也是挂载任务,只不过是在任务函数等待消息队列,发现消息列表 MsgQ 里面没有消息,就将任务挂载等待列表,然后等待信息。
消息列表MsgQ 也是个结构体,它是消息的根节点
typedef struct os_msg_q OS_MSG_Q;
struct os_msg_q { /* OS_MSG_Q */
OS_MSG InPtr; / 指向下一个要插入到队列中的OS_MSG的指针 */
OS_MSG OutPtr; / 指向下一个要从队列中提取的OS_MSG的指针 /
OS_MSG_QTY NbrEntriesSize; / 变量—存储最大可以有多少条消息 /
OS_MSG_QTY NbrEntries; / 变量—记录当前消息的数量 /
OS_MSG_QTY NbrEntriesMax; / Peak number of entries in the queue */
};
消息列表MsgQ 这个结构体功能是很直白的:
情况1
就是如果发现没有任务在等待消息的时候,就把发布的消息暂时放到这个消息列表MsgQ 上面,每一包消息都是先整合到 OS_MSG 这个结构体里面,以后消息就是代表OS_MSG 这种数据类型的结构体
typedef struct os_msg OS_MSG;**
struct os_msg {
OS_MSG NextPtr; / 用来插入到消息列表MsgQ的指针 */
void MsgPtr; / 存储消息内容的指针 /
OS_MSG_SIZE MsgSize; / 存储消息内容长度的变量 /
CPU_TS MsgTS; / Time stamp of when message was sent */
};
这里的整合就是赋值到OS_MSG对应的成员里面,然后将OS_MSG里面的 *NextPtr指针,通过单向链表插入或者删除到消息列表MsgQ
情况2
如果在一个任务发布消息发现有等待列表有任务在等待这个消息,先将等待列表的任务从等待列表移除,最后是将消息的指针和消息长度赋值给等待列表上面挂载任务的任务控制块的两个成员,等任务切换访问任务控制块这两个成员就可以读出消息的内容了
struct os_tcb {
#if OS_MSG_EN > 0u
void MsgPtr; / 消息内容指针 /
OS_MSG_SIZE MsgSize; / 消息内容长度变量 */
#endif
};
我相信对消息队列还有一些谜团还没说到
问题1 那么消息OS_MSG 插入到消息列表MsgQ 是基于什么原则的
问题2 当有任务等待消息,发现消息列表上面有消息OS_MSG 又怎么从消息列表MsgQ 上面取消息的
问题3 如果在一个任务发布消息发现有等待列表有任务在等待这个消息,先将等待列表的任务从等待列表移除,最后是将消息的指针和消息长度赋值给等待列表上面挂载任务的任务控制块的两个成员,等任务切换访问任务控制块这两个成员就可以读出消息的内容了,具体是怎么操作的
解答问题1
在发布消息函数 OSQPost();里面有opt这个选项是用来确定插入原则的
opt
- OS_OPT_POST_FIFO 将消息插入到 消息列表MsgQ 的队末指针 OS_MSG *InPtr;
- OS_OPT_POST_LIFO 将消息插入到 消息列表MsgQ 的队前指针 OS_MSG *OutPtr;
解答问题2
从消息列表MsgQ 的队前指针OS_MSG *OutPtr; 取消息
等一下在对消息列表的插入和删除函数代码就可以看出了
最后的插入效果看图
看代码实际操作
插入消息到消息列表函数是 OS_MsgQPut (OS_MSG_Q *p_msg_q, //消息列表指针
void *p_void, //消息内容指针
OS_MSG_SIZE msg_size, //消息内容长度
OS_OPT opt, //插入选项
CPU_TS ts,
OS_ERR *p_err) //返回的错误值
void OS_MsgQPut (OS_MSG_Q *p_msg_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
CPU_TS ts,
OS_ERR *p_err)
{
OS_MSG *p_msg; /*消息指针 */
OS_MSG *p_msg_in;
if (p_msg_q->NbrEntries >= p_msg_q->NbrEntriesSize) { //消息队列的消息数量不能超过预设最大值
*p_err = OS_ERR_Q_MAX; /*返回错误消息队列满了 */
return;
}
if (OSMsgPool.NbrFree == (OS_MSG_QTY)0) { /*消息池是否为空 */
*p_err = OS_ERR_MSG_POOL_EMPTY;
return;
}
/*消息池非空 */
/*这里注意 p_msg是指针,没有给指针开辟内存空间,而内存空间是消息池提供的*/
p_msg = OSMsgPool.NextPtr; /*从消息池里面移除一个内存块给p_msg 指针让它有指针即为 OS_MSG消息 开辟了空间 */
OSMsgPool.NextPtr = p_msg->NextPtr;
OSMsgPool.NbrFree--;
OSMsgPool.NbrUsed++;
if (OSMsgPool.NbrUsedMax < OSMsgPool.NbrUsed) {
OSMsgPool.NbrUsedMax = OSMsgPool.NbrUsed;
}
if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) { /*消息队列的消息为空,则现在这个消息作为第一个消息插入到消息队列 */
p_msg_q->InPtr = p_msg;
p_msg_q->OutPtr = p_msg;
p_msg_q->NbrEntries = (OS_MSG_QTY)1;
}
else { //消息队列有消息
if ((opt & OS_OPT_POST_LIFO) == OS_OPT_POST_FIFO){ /*把当前消息插入到消息队列的队末*/
p_msg_in = p_msg_q->InPtr; /* FIFO */
p_msg_in->NextPtr = p_msg;
p_msg->NextPtr = (OS_MSG *)0;
p_msg_q->InPtr = p_msg; /*队末指针 InPtr 往右移了 */
} else { //把当前消息插入到消息队列的队前
p_msg->NextPtr = p_msg_q->OutPtr; /* LIFO */
p_msg_q->OutPtr = p_msg; /*队前指针 OutPtr 往右移了 */
}
p_msg_q->NbrEntries++; //消息队列里的消息数量加1
}
if (p_msg_q->NbrEntriesMax < p_msg_q->NbrEntries) {
p_msg_q->NbrEntriesMax = p_msg_q->NbrEntries;
}
p_msg->MsgPtr = p_void; /*给该消息填写消息内容
p_msg->MsgSize = msg_size; //给该消息填写消息大小
p_msg->MsgTS = ts;
*p_err = OS_ERR_NONE;
}
从消息列表移除消息的函数是 *OS_MsgQGet (OS_MSG_Q *p_msg_q, //消息队列指针
OS_MSG_SIZE *p_msg_size, //消息大小变量指针
CPU_TS *p_ts,
OS_ERR *p_err) //f返回的错误值
看代码
void *OS_MsgQGet (OS_MSG_Q *p_msg_q,
OS_MSG_SIZE *p_msg_size,
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_MSG *p_msg;
void *p_void;
if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) { // 如果消息队列没有消息
*p_msg_size = (OS_MSG_SIZE)0; //返回消息长度为0
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS )0;
}
*p_err = OS_ERR_Q_EMPTY; //错误类型为“队列没消息”
return ((void *)0);
}
/* 如果消息队列有消息 */
p_msg = p_msg_q->OutPtr; //注意重点:从消息列表的出口端提取消息,所以又消息是从消息列表MsgQ 的队前指针OS_MSG *OutPtr 取消息
p_void = p_msg->MsgPtr; //提取消息内容
*p_msg_size = p_msg->MsgSize; //提取消息长度
if (p_ts != (CPU_TS *)0) {
*p_ts = p_msg->MsgTS;
}
p_msg_q->OutPtr = p_msg->NextPtr; //修改队列的出队指针
if (p_msg_q->OutPtr == (OS_MSG *)0) { //如果队列没有消息了
p_msg_q->InPtr = (OS_MSG *)0; //清零出队指针
p_msg_q->NbrEntries = (OS_MSG_QTY)0; //清零消息数
} else { //如果队列还有消息
p_msg_q->NbrEntries--; //队列的消息数减1
}
/* 从消息队列提取完消息信息后,将消息释放回消息池供继续使用 */
p_msg->NextPtr = OSMsgPool.NextPtr; /*//消息插回消息池 Return message control block to free list */
OSMsgPool.NextPtr = p_msg;
OSMsgPool.NbrFree++; //消息池的可用消息数加1
OSMsgPool.NbrUsed--; //消息池的已用消息数减1
*p_err = OS_ERR_NONE; //错误类型为“无错误”
return (p_void);
}
补充一点就是消息池又是怎么回事
首先我们插入一个消息OS_MSG 到消息列表MsgQ 里面,构建这个消息OS_MSG的结构体它是需要一点的内存的,我们看上面的函数,定义一个消息OS_MSG它基本上是一个指针,它要有指向,而这个指向肯定是一段内存空间的首地址,而消息池是专门为消息OS_MSG 开辟内存空间,
那消息池是怎么样的,什么时候初始化的
#define OS_CFG_MSG_POOL_SIZE 100u
OS_MSG OSCfg_MsgPool [OS_CFG_MSG_POOL_SIZE]; //定义了100个消息池
typedef struct os_msg_pool OS_MSG_POOL; //消息池列表
struct os_msg_pool { /* OS_MSG POOL */
OS_MSG *NextPtr; /*消息池指针 /
OS_MSG_QTY NbrFree; / 存储消息池消息数量的变量 /
OS_MSG_QTY NbrUsed; / 存储当前用了消息池多少消息 /
OS_MSG_QTY NbrUsedMax; / Peak number of messages used */
};
OS_MSG_POOL OSMsgPool; //消息池列表
从上面可以看出就是消息池列表上面挂载早就定义好的100个消息池,通过消息池的 *NextPtr指针插入到消息池列表OSMsgPool
在UCOSIII初始化函数 OSInit (OS_ERR *p_err);就对消息池作列表了初始化
看代码
OS_MSG * const OSCfg_MsgPoolBasePtr = (OS_MSG *)&OSCfg_MsgPool[0]; //OSCfg_MsgPoolBasePtr 指向的是第一个消息池
void OSInit (OS_ERR *p_err)
{
#if (OS_MSG_EN) > 0u
OS_MsgPoolInit(p_err); //消息池列表初始化
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
}
void OS_MsgPoolInit (OS_ERR *p_err)
{
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u
if (OSCfg_MsgPoolBasePtr == (OS_MSG *)0) {
*p_err = OS_ERR_MSG_POOL_NULL_PTR;
return;
}
if (OSCfg_MsgPoolSize == (OS_MSG_QTY)0) {
*p_err = OS_ERR_MSG_POOL_EMPTY;
return;
}
#endif
OS_MsgPoolCreate(OSCfg_MsgPoolBasePtr,
OSCfg_MsgPoolSize); //消息池列表挂载100个消息池函数,这里就不展开了,其实就是插入100个节点不难看懂
//消息池列表剩余成员初始化
OSMsgPool.NextPtr = OSCfg_MsgPoolBasePtr; //OSCfg_MsgPoolBasePtr 指向的是第一个消息池
OSMsgPool.NbrFree = OSCfg_MsgPoolSize;
OSMsgPool.NbrUsed = (OS_MSG_QTY)0;
OSMsgPool.NbrUsedMax = (OS_MSG_QTY)0;
*p_err = OS_ERR_NONE;
}
解答问题3
从发送消息函数 OSQPost(); 和 等待消息函数 OSQPend(); 内部去解析该函数
void *OSQPend (OS_Q p_q, / 消息队列指针 /
OS_TICK timeout, / 等待消息需要的节拍数 /
OS_OPT opt, / 等待消息选项 */
OS_MSG_SIZE p_msg_size, / 返回消息内容的长度的变量 */
CPU_TS *p_ts,
OS_ERR p_err) / 返回的错误值 */
根据 opt 与 timeout 的结合可以有多种等待消息的方式
Opt
OS_OPT_PEND_BLOCKING 就是指定消息无效时,任务挂起以等待消息
OS_OPT_PEND_NON_BLOCKING 消息无效时任务直接返回,不挂起任务,继续执行当前任务
timeout=0 + Opt=OS_OPT_PEND_BLOCKING | 不能立即收到消息,任务挂起以等待消息并阻塞任务 ,直到等到消息就解挂 ,要是等待消息马上就可以获取到信号量是不阻塞任务的 |
---|---|
timeout>0 + Opt=OS_OPT_PEND_BLOCKING | 要是等待消息马上就可以获取到消息是不阻塞任务的,要是不能立即收到信号量,任务挂起以等待消息并阻塞任务,在等待完 timeout 个节拍数还没有收到消息就解挂,继续运行任务,如果在这些节拍数只能收到信号量也解挂 |
timeout不管是多少 + Opt=OS_OPT_PEND_NON_BLOCKING | 不管能不能立即收到消息,任务都不堵塞不挂起,继续运行 |
注意:该函数不能在中断里面使用,该函数存在阻塞因素,而中断是不能注意长时间阻塞的,否则会进入硬件中断程序跑飞了”
看代码
void *OSQPend (OS_Q *p_q,
OS_TICK timeout,
OS_OPT opt,
OS_MSG_SIZE *p_msg_size,
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_PEND_DATA pend_data;
void *p_void;
CPU_SR_ALLOC();
//参数检查代码省略
CPU_CRITICAL_ENTER();
/************improtance function***************/
p_void = OS_MsgQGet(&p_q->MsgQ, /* 该函数从消息队列获取一个消息*/
p_msg_size, //这个变量存储了消息内容的长度
p_ts,
p_err);
if (*p_err == OS_ERR_NONE) { //如果获取消息成功
CPU_CRITICAL_EXIT();
return (p_void); /*返回消息内容即就是消息队列的首地址*/
}
/* 如果获取消息不成功 */
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { /* 如果选择了不堵塞任务 */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_WOULD_BLOCK; /* 返回错误类型就是OS_ERR_PEND_WOULD_BLOCK*/
return ((void *)0); //这里返回的参数为0 其实就是让你返回继续执行任务
} else { //如果选择了堵塞任务
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /*这是调度器被锁 */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED;
return ((void *)0);
}
}
//堵塞任务 ---调度器不被锁
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
OS_Pend(&pend_data, /*堵塞当前任务,等待消息队列 */
(OS_PEND_OBJ *)((void *)p_q), //并将当前任务脱离就绪列表,插入到等待列表和节拍列表
OS_TASK_PEND_ON_Q,
timeout);
OS_CRITICAL_EXIT_NO_SCHED();
OSSched(); /*找到当前就绪态并优先级最高的任务,切换到这个任务 */
/* 当前任务(获得消息队列的消息)得以继续运行 */
CPU_CRITICAL_ENTER(); //关中断
switch (OSTCBCurPtr->PendStatus) { //根据当前运行任务的等待状态分类处理
case OS_STATUS_PEND_OK: //如果等待状态正常 (注意这里是重点)
p_void = OSTCBCurPtr->MsgPtr; //从(发布时放于)任务控制块提取消息
*p_msg_size = OSTCBCurPtr->MsgSize; //提取消息大小
if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空
*p_ts = OSTCBCurPtr->TS; //获取任务等到消息时的时间戳
}
*p_err = OS_ERR_NONE; //错误类型为“无错误”
break; //跳出
case OS_STATUS_PEND_ABORT: //如果等待被中止
p_void = (void *)0; //返回消息内容为空
*p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为0
if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空
*p_ts = OSTCBCurPtr->TS; //获取等待被中止时的时间戳
}
*p_err = OS_ERR_PEND_ABORT; //错误类型为“等待被中止”
break; //跳出
case OS_STATUS_PEND_TIMEOUT: //如果等待超时
p_void = (void *)0; //返回消息内容为空
*p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为0
if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空
*p_ts = (CPU_TS )0; //清零 p_ts
}
*p_err = OS_ERR_TIMEOUT; //错误类型为“等待超时”
break; //跳出
case OS_STATUS_PEND_DEL: //如果等待的内核对象被删除
p_void = (void *)0; //返回消息内容为空
*p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为0
if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空
*p_ts = OSTCBCurPtr->TS; //获取对象被删时的时间戳
}
*p_err = OS_ERR_OBJ_DEL; //错误类型为“等待对象被删”
break; //跳出
default: //如果等待状态超出预期
p_void = (void *)0; //返回消息内容为空
*p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为0
*p_err = OS_ERR_STATUS_INVALID; //错误类型为“状态非法”
break; //跳出
}
CPU_CRITICAL_EXIT(); //开中断
return (p_void); //返回消息内容
}
简单说一下运行流程
先在 OS_MsgQGet(); 函数中查找消息列表里面有没有消息,有就退出函数,没有消息,如果选择无限等待消息就通过 OS_Pend();函数将任务插入到等待列表,然后任务切换
直到收到消息
在switch的 OS_STATUS_PEND_OK选项表示正确收到消息
case OS_STATUS_PEND_OK: //如果等待状态正常 (注意这里是重点)
p_void = OSTCBCurPtr->MsgPtr; //从(发布时放于)任务控制块提取消息
*p_msg_size = OSTCBCurPtr->MsgSize; //提取消息大小
从这里可以看出提取消息是通过任务句柄任务控制块的两个成员的,至于什么时候给这两个成员看发送消息函数
消息发送函数OSQPost()
void OSQPost (OS_Q *p_q, //消息队列指针
void *p_void, //消息内容指针
OS_MSG_SIZE msg_size, //消息内容长度变量
OS_OPT opt, //发布消息选项
OS_ERR *p_err); //返回错误值
根据 opt 可以有多种发送消息的方式
opt | 对应功能 |
---|---|
1、OS_OPT_POST_FIFO | 发布的消息放到消息列表队末指针 *InPrt 在发现等待列表没有任务在等待消息的时候,直到有任务等待消息,再从消息列表那里取出消息;那反之等待列表有任务等待消息直接将消息直接发给等待列表的第一个任务 |
2、OS_OPT_POST_LIFO | 发布的消息放到消息列表队前指针 *OutPrt 在发现等待列表没有任务在等待消息的时候,直到有任务等待消息,再从消息列表那里取出消息;那反之等待列表有任务等待消息直接将消息直接发给等待列表的第一个任务 |
3、OS_OPT_POST_FIFO + OS_OPT_POST_ALL | 发布的消息放到消息列表队末指针 *InPrt 在发现等待列表没有任务在等待消息的时候,直到有任务等待消息,再从消息列表那里取出消息;那反之等待列表有任务等待消息直接将消息直接发给等待列表的所有任务 |
4、OS_OPT_POST_LIFO + OS_OPT_POST_ALL | 发布的消息放到消息列表队前指针 *OutPrt 在发现等待列表没有任务在等待消息的时候,直到有任务等待消息,再从消息列表那里取出消息;那反之等待列表有任务等待消息直接将消息直接发给等待列表的所有任务 |
5、OS_OPT_POST_FIFO + OS_OPT_POST_NO_SCHED | 发布的消息放到消息列表队末指针 *InPrt 在发现等待列表没有任务在等待消息的时候,直到有任务等待消息,再从消息列表那里取出消息;那反之等待列表有任务等待消息直接将消息直接发给等待列表的第一个任务,但是不进行任务调度,继续运行当前任务 |
6、OS_OPT_POST_LIFO + OS_OPT_POST_NO_SCHED | 发布的消息放到消息列表队前指针 *OutPrt 在发现等待列表没有任务在等待消息的时候,直到有任务等待消息,再从消息列表那里取出消息;那反之等待列表有任务等待消息直接将消息直接发给等待列表的第一个任务,但是不进行任务调度,继续运行当前任务 |
7、OS_OPT_POST_FIFO + OS_OPT_POST_ALL + OS_OPT_POST_NO_SCHED | 发布的消息放到消息列表队末指针 *InPrt 在发现等待列表没有任务在等待消息的时候,直到有任务等待消息,再从消息列表那里取出消息;那反之等待列表有任务等待消息直接将消息直接发给等待列表的所有任务,但是不进行任务调度,继续运行当前任务 |
8、OS_OPT_POST_FIFO + OS_OPT_POST_ALL + OS_OPT_POST_NO_SCHED | 发布的消息放到消息列表队前指针 *OutPrt 在发现等待列表没有任务在等待消息的时候,直到有任务等待消息,再从消息列表那里取出消息;那反之等待列表有任务等待消息直接将消息直接发给等待列表的所有任务,但是不进行任务调度,继续运行当前任务 |
注意:1–4个选项都是没有选择OS_OPT_POST_NO_SCHED,那么意味如果等待列表有任务在等待消息,当前任务是直接把消息发布给等待列表的一个或多个任务,然后进行任务切换,但是如果发布消息的任务比等待消息的任务优先级高,那么即便任务切换也是相对应切换回原来的任务相当于不进行任务调度,继续运行当前任务;反之优先级低就会切换到等待消息那个任务
看代码
void OSQPost (OS_Q *p_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
OS_ERR *p_err)
{
CPU_TS ts;
/* 参数检查代码省略 */
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) {
OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_Q, /*中断级发送消息用该函数 */
(void *)p_q,
(void *)p_void,
(OS_MSG_SIZE)msg_size,
(OS_FLAGS )0,
(OS_OPT )opt,
(CPU_TS )ts,
(OS_ERR *)p_err);
return;
}
#endif
/*********************improtance funtion*********************************/
OS_QPost(p_q, /* 任务级发送消息用该函数 */
p_void,
msg_size,
opt,
ts,
p_err);
}
主要内容在 OS_QPost();函数
void OS_QPost (OS_Q *p_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
CPU_TS ts,
OS_ERR *p_err)
{
OS_OBJ_QTY cnt;
OS_OPT post_type;
OS_PEND_LIST *p_pend_list;
OS_PEND_DATA *p_pend_data;
OS_PEND_DATA *p_pend_data_next;
OS_TCB *p_tcb;
CPU_SR_ALLOC();
OS_CRITICAL_ENTER();
p_pend_list = &p_q->PendList; //取出该队列的等待列表
if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) { /*NbrEntries=0 (说明没有任务在等待消息) */
if ((opt & OS_OPT_POST_LIFO) == (OS_OPT)0) { /*把消息发布到队列的末端 */
post_type = OS_OPT_POST_FIFO;
} else { //把消息发布到队列的前端
post_type = OS_OPT_POST_LIFO;
}
OS_MsgQPut(&p_q->MsgQ, /*//把消息放入消息队列 */
p_void,
msg_size,
post_type,
ts,
p_err);
OS_CRITICAL_EXIT();
return; //返回,执行完毕
/* 如果有任务在等待该队列说明有任务在等待消息 */
if ((opt & OS_OPT_POST_ALL) != (OS_OPT)0) { /*如果要把消息发布给所有等待任务 */
cnt = p_pend_list->NbrEntries; /*获取等待任务数目 */
} else { //如果要把消息发布给一个等待任务
cnt = (OS_OBJ_QTY)1; /*要处理的任务数目为1 */
}
p_pend_data = p_pend_list->HeadPtr; //获取等待列表的头部(第一个任务)
while (cnt > 0u) { //根据要发布的任务数目逐个发布
p_tcb = p_pend_data->TCBPtr;
p_pend_data_next = p_pend_data->NextPtr;
OS_Post((OS_PEND_OBJ *)((void *)p_q), //把消息发布给任务
p_tcb,
p_void,
msg_size,
ts);
p_pend_data = p_pend_data_next;
cnt--;
}
OS_CRITICAL_EXIT_NO_SCHED();
if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) { //如果没选择“发布完不调度任务”
OSSched(); /*任务调度*/
}
*p_err = OS_ERR_NONE; //错误类型为“无错误”
}
void OS_Post (OS_PEND_OBJ *p_obj,
OS_TCB *p_tcb,
void *p_void,
OS_MSG_SIZE msg_size,
CPU_TS ts)
{
switch (p_tcb->TaskState) {
//代码省略
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) { //等待多个内核对象(这里是内嵌消息队列和内嵌消息信号量才执行这里)
OS_Post1(p_obj,
p_tcb,
p_void,
msg_size,
ts);
} else { //消息队列执行下面
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = p_void; /*将消息的内容放到任务控制块*/
p_tcb->MsgSize = msg_size; /*将消息的大小放到任务控制块 */
#endif
p_tcb->TS = ts;
}
if (p_obj != (OS_PEND_OBJ *)0) { //等待的内核对象非空
OS_PendListRemove(p_tcb); /*删除等待列表里面等待的任务 */
//代码省略
}
OS_TaskRdy(p_tcb); /*如果是有限等待还要删除时钟节拍列表的任务同时任务处于就绪态*/
p_tcb->TaskState = OS_TASK_STATE_RDY; //任务状态是就绪态
p_tcb->PendStatus = OS_STATUS_PEND_OK; /*任务等待的状态标志为o */
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /*等待的内核对象是空 */
break;
//代码省略
}
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
p_tcb->PendStatus = OS_STATUS_PEND_OK; /* Clear pend status */
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
break;
default:
break;
}
}
简单说一下发送流程
如果等待列表没有等待消息的任务,将发布的消息放到消息列表上通过OS_MsgQPut()函数,如果有任务在等待列表,将消息发布给等待列表的任务通过OS_Post()函数
在OS_Post()函数的
switch选项
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = p_void; /将消息的内容放到任务控制块/
p_tcb->MsgSize = msg_size; /*将消息的大小放到任务控制块 */
#endif
这个这处代码体现的消息是给等待列表的任务是通过任务控制块的
讲了那么多如果我们要成功使用消息队列,还要先创建一个消息队列函数是OSQCreate()
void OSQCreate (OS_Q *p_q, //消息队列指针
CPU_CHAR *p_name, //消息队列名称
OS_MSG_QTY max_qty, //消息队列长度,消息队列的长度不能为0
OS_ERR *p_err) //返回的错误值
void OSQCreate (OS_Q *p_q,
CPU_CHAR *p_name,
OS_MSG_QTY max_qty,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
//代码省略
OS_CRITICAL_ENTER();
p_q->Type = OS_OBJ_TYPE_Q; /* 标记创建对象的数据结构为消息队列 */
p_q->NamePtr = p_name; //标记队列的名字
OS_MsgQInit(&p_q->MsgQ, /*初始化消息队列 其实就是初始化消息队列的那个结构体里面的成员而已*/
max_qty);
OS_PendListInit(&p_q->PendList); /* 初始化消息队列的等待列表 */
//每个消息队列都有一个等待列表,凡是等待该消息队列的的任务都会被插入到这个等待列表,
//代码省略
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_NONE;
}
整体总结一下