2. 消息队列创建与删除:msgQCreate与msgQDelete
好,咱们直接进入正题。消息队列的创建和删除,是使用消息队列的第一步,也是基础中的基础。我见过不少新手,上来就发消息、收消息,结果队列都没创建成功,程序跑起来就崩了。所以,咱们先把地基打牢。
2.1 msgQCreate 函数详解
这个函数,说白了就是向VxWorks内核申请一个消息队列的资源。它的原型长这样:
MSG_Q_ID msgQCreate
(
int maxMsgs, /* 队列最多能存多少条消息 */
int maxMsgLength, /* 每条消息的最大字节数 */
int options /* 队列的选项,比如 FIFO 还是优先级 */
)
嗯,三个参数,一个返回值。咱们一个一个说。
2.1.1 参数一:maxMsgs
这个参数指定了队列最多能同时容纳多少条消息。注意,是「最多」。我个人的习惯是,根据系统中可能出现的最大消息积压量,再留出 20%-30% 的余量。
举个例子:
- 你的发送任务每 10ms 发一条消息
- 接收任务每 50ms 才能处理一条
- 那么 50ms 内会积压 5 条消息
- 我一般会设成 8 或 10,留点余量
2.1.2 参数二:maxMsgLength
这个参数指定了每条消息的最大字节数。注意,是「最大」。你实际发送的消息可以小于这个值,但不能大于。
我在项目中遇到过一个问题:有个同事把 maxMsgLength 设成了 256,但实际发送的消息只有 10 个字节。结果每个消息都占用了 256 字节的缓冲区,内存浪费很严重。你想想看,如果 maxMsgs 是 100,那光消息缓冲区就占了 25KB,实际上只用到了 1KB。
所以我的建议是:
- 如果消息长度固定,就设成那个固定值
- 如果消息长度变化,按最长的来,但别太离谱
- 如果消息内容很少,可以考虑用指针传递,而不是拷贝数据
2.1.3 参数三:options
这个参数决定了消息队列的行为方式。常用的选项有两个:
| 选项 | 含义 | 说明 |
|---|---|---|
| MSG_Q_FIFO | 先进先出 | 消息按到达顺序排队,先到的先被取走 |
| MSG_Q_PRIORITY | 优先级队列 | 消息按优先级排队,优先级高的先被取走 |
我个人习惯,90% 的场景用 MSG_Q_FIFO。为什么?因为简单、直观、不容易出错。优先级队列虽然听起来很高级,但用不好容易导致优先级反转或者低优先级消息永远得不到处理。
2.2 返回值处理
msgQCreate 返回一个 MSG_Q_ID 类型的值。这个值要么是一个有效的队列 ID,要么是 NULL。
嗯,这里要注意:
- 返回 NULL:创建失败。原因可能是内存不足,或者参数不合理
- 返回非 NULL:创建成功。但这个 ID 一定要保存好,后面所有操作都靠它
我曾经见过一个案例:有人创建了队列,但没检查返回值,直接拿着 NULL 去发消息。结果系统直接 panic 了。所以,一定要检查返回值:
MSG_Q_ID myQueue = msgQCreate(10, 256, MSG_Q_FIFO);
if (myQueue == NULL) {
/* 创建失败,处理错误 */
printf("消息队列创建失败!\n");
return ERROR;
}
2.3 msgQDelete 函数
有创建就有删除。msgQDelete 就是用来释放消息队列资源的。原型很简单:
STATUS msgQDelete
(
MSG_Q_ID msgQId /* 要删除的队列 ID */
)
这个函数会:
- 释放队列占用的所有内存
- 丢弃队列中所有未处理的消息
- 如果有任务正在等待接收消息,这些任务会返回 ERROR
返回值处理也很简单:
- OK:删除成功
- ERROR:删除失败,可能是队列 ID 无效
2.4 一个完整的例子
说了这么多,咱们来看个完整的例子。这个例子展示了从创建到删除的完整流程:
#include <vxWorks.h>
#include <msgQLib.h>
MSG_Q_ID g_myQueue; /* 全局变量,方便其他任务使用 */
STATUS initMessageQueue(void)
{
/* 创建队列:最多存 20 条,每条最大 100 字节,FIFO 模式 */
g_myQueue = msgQCreate(20, 100, MSG_Q_FIFO);
if (g_myQueue == NULL) {
printf("队列创建失败,内存不足?\n");
return ERROR;
}
printf("队列创建成功,ID: 0x%x\n", (int)g_myQueue);
return OK;
}
STATUS cleanupMessageQueue(void)
{
STATUS status;
if (g_myQueue == NULL) {
return OK; /* 已经删过了 */
}
/* 删除前,通知所有任务停止使用队列 */
/* 这里省略通知逻辑,实际项目中需要 */
status = msgQDelete(g_myQueue);
if (status == OK) {
g_myQueue = NULL; /* 置空,防止野指针 */
printf("队列删除成功\n");
} else {
printf("队列删除失败\n");
}
return status;
}
- 创建时检查返回值,别拿 NULL 当 ID 用
- 删除后把 ID 置 NULL,防止误用
- 参数配置要合理,别浪费内存也别不够用
- 删除前确保没有任务在等待
好了,消息队列的创建和删除就这些内容。说白了就是「申请资源」和「释放资源」两个动作,但细节都在参数配置和错误处理里。下一节咱们聊聊怎么往队列里发消息和收消息,那才是真正干活的地方。