2. 消息队列创建与删除:msgQCreate()与msgQDelete()函数详解
消息队列,说白了就是任务之间传递数据的“信箱”。
你想想看,一个任务往里面扔消息,另一个任务从里面取消息。这个过程,在VxWorks里全靠msgQCreate()和msgQDelete()这对函数来管理。
我个人习惯,每次写通信模块时,第一件事就是先把队列创建好。为什么?因为后面所有收发逻辑都依赖这个队列句柄。队列没建好,后面全是白搭。
2.1 msgQCreate():创建消息队列
这个函数的原型长这样:
MSG_Q_ID msgQCreate
(
int maxMsgs, /* 队列最多能放多少条消息 */
int maxMsgLength, /* 每条消息的最大字节数 */
int options /* 队列选项,决定消息排队方式 */
)
返回值是一个MSG_Q_ID类型的句柄。如果创建失败,返回NULL。
参数详解:
| 参数 | 含义 | 我踩过的坑 |
|---|---|---|
| maxMsgs | 队列容量,即最多能缓存的消息条数 | 设太小容易丢消息,设太大浪费内存 |
| maxMsgLength | 每条消息的最大长度(字节) | 注意是字节数,不是消息条数 |
| options | MSG_Q_FIFO 或 MSG_Q_PRIORITY | 选错会导致消息顺序混乱 |
options 选项说明:
MSG_Q_FIFO(0):先进先出。谁先发,谁先被取走。我大部分场景都用这个。MSG_Q_PRIORITY(1):按消息优先级排队。优先级高的消息,即使后发也会被先取走。
嗯,这里要注意:maxMsgs和maxMsgLength共同决定了队列占用的内存大小。VxWorks内部会分配一块连续内存来存放所有消息。计算公式是:总内存 = maxMsgs * (maxMsgLength + 4)。多出来的4字节是内部管理用的。
核心要点:
创建队列时,VxWorks会一次性分配好所有消息缓冲区。所以队列创建后,maxMsgs和maxMsgLength就不能改了。如果你需要动态调整,只能先删掉再重建。
2.2 msgQDelete():删除消息队列
有创建就有删除。函数原型很简单:
STATUS msgQDelete
(
MSG_Q_ID msgQId /* 要删除的队列句柄 */
)
返回值:OK(0)表示成功,ERROR(-1)表示失败。
这个函数干三件事:
- 释放队列占用的所有内存(包括消息缓冲区)
- 唤醒所有正在等待该队列的任务(它们会收到错误码)
- 将队列句柄置为无效
警告:
千万不要在中断服务程序(ISR)中调用msgQDelete()!我曾经犯过这个错,结果系统直接挂掉。因为删除操作可能引起任务调度,而ISR里不允许调度。
2.3 实战代码示例
下面是我在一个数据采集项目中用到的典型代码。任务A采集传感器数据,通过队列发给任务B处理。
/* 全局队列句柄 */
MSG_Q_ID g_dataQueue;
/* 创建队列 */
STATUS createDataQueue(void)
{
g_dataQueue = msgQCreate(10, sizeof(SensorData), MSG_Q_FIFO);
if (g_dataQueue == NULL)
{
/* 创建失败,打印错误 */
logMsg("Failed to create data queue!\n", 0,0,0,0,0);
return ERROR;
}
logMsg("Data queue created successfully.\n", 0,0,0,0,0);
return OK;
}
/* 删除队列 */
STATUS deleteDataQueue(void)
{
STATUS status;
if (g_dataQueue == NULL)
{
return OK; /* 队列不存在,直接返回 */
}
status = msgQDelete(g_dataQueue);
if (status != OK)
{
logMsg("Failed to delete data queue!\n", 0,0,0,0,0);
return ERROR;
}
g_dataQueue = NULL; /* 防止野指针 */
logMsg("Data queue deleted.\n", 0,0,0,0,0);
return OK;
}
我的小技巧:
删除队列后,一定要把句柄置为NULL。不然其他任务拿着过期的句柄去操作,轻则返回错误,重则直接宕机。我早期项目里因为这个bug排查了整整两天。
2.4 避坑指南
这些年用下来,我总结了几条血泪教训:
- 内存碎片问题:频繁创建删除队列,会导致内存碎片。我建议在系统初始化时一次性创建好所有队列,运行期间不要反复创建删除。
- 队列容量估算:
maxMsgs设多少合适?我一般按峰值消息速率的2倍来算。比如任务A每秒最多发50条消息,任务B每秒处理30条,那队列容量至少设50,保险点设100。 - 消息长度对齐:
maxMsgLength最好设成4字节的整数倍。VxWorks内部做内存对齐,非对齐长度会浪费空间。 - 句柄有效性检查:每次使用队列前,检查句柄是否为NULL。别问我为什么强调这个——我曾经在系统运行几天后,因为某个模块异常删除了队列,导致其他模块全部崩溃。
2.5 性能考量
创建和删除操作本身很快,一般几十微秒就能完成。但要注意:
msgQCreate()会调用malloc()分配内存。如果系统内存碎片严重,分配时间可能变长。msgQDelete()会唤醒所有等待任务。如果等待任务很多(比如几十个),删除操作可能触发大量任务切换,导致短暂的系统卡顿。
所以我的建议是:队列的生命周期尽量和任务保持一致。任务启动时创建,任务退出时删除。别在热路径上频繁创建删除。
一句话总结:
msgQCreate()是通信的起点,msgQDelete()是终点。创建时想清楚容量和选项,删除时记得清空句柄。这两步做好了,后面的收发操作才能稳如老狗。