在 uC/OS(Micro-Controller Operating System)中,队列(Queue)是任务间通信的核心机制之一,用于实现数据缓冲和异步消息传递。uC/OS 的队列设计兼顾了实时性和内存效率,以下从代码结构、核心接口和实现特点进行分析:
一、uC/OS 队列的核心数据结构
uC/OS 中队列的控制块(OS_Q
)定义了队列的全部状态信息,关键成员如下(以 uC/OS-III 为例):
typedef struct os_q {
CPU_CHAR Name[OS_OBJ_NAME_LEN]; // 队列名称(调试用)
OS_MSG_Q MsgQ; // 消息队列容器
OS_OBJ_QTY MaxMsg; // 最大消息数
OS_MSG_SIZE MaxMsgSize; // 单条消息最大字节数
OS_OBJ_TYPE Type; // 对象类型(标识为队列)
// 等待队列(任务阻塞时的等待链表)
OS_PEND_LIST PendList;
// 其他状态变量(如是否使用动态内存、计数器等)
OS_FLAGS Flags;
CPU_INT32U MsgQty; // 当前消息数量
// ...
} OS_Q;
OS_MSG_Q
:实际存储消息的缓冲区,通常为环形队列结构PendList
:记录等待该队列的任务(用于阻塞机制)
二、核心接口及实现分析
uC/OS 的队列操作接口封装在os_q.c
文件中,核心功能如下:
1. 队列创建(OSQCreate()
)
功能:初始化队列控制块,分配消息缓冲区。
关键代码逻辑:
OS_Q *OSQCreate(OS_Q *p_q,
CPU_CHAR *p_name,
OS_OBJ_QTY max_msg,
OS_MSG_SIZE max_msg_size,
OS_ERR *p_err)
{
// 参数校验(空指针、长度合法性)
if (p_q == (OS_Q *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((OS_Q *)0);
}
// 初始化队列控制块成员
p_q->MaxMsg = max_msg;
p_q->MaxMsgSize = max_msg_size;
p_q->MsgQty = 0u;
// 初始化等待链表(无任务等待)
OS_PendListInit(&p_q->PendList);
// 初始化消息缓冲区(环形队列)
OS_MsgQInit(&p_q->MsgQ, max_msg, max_msg_size, p_err);
// ...
return (p_q);
}
特点:
- 支持静态创建(用户提供
OS_Q
结构体)和动态创建(内部调用malloc
) - 需指定最大消息数和单条消息长度,避免内存溢出
2. 发送消息(OSQPost()
)
功能:向队列尾部发送消息,支持阻塞 / 非阻塞模式。
核心逻辑:
OS_OBJ_QTY OSQPost(OS_Q *p_q,
void *p_msg,
OS_MSG_SIZE msg_size,
OS_FLAGS opt,
OS_ERR *p_err)
{
CPU_SR_ALLOC(); // 用于临界区保护
CPU_CRITICAL_ENTER(); // 关中断(确保操作原子性)
// 检查队列是否已满
if (p_q->MsgQty >= p_q->MaxMsg) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_Q_FULL;
return (0u);
}
// 复制消息到队列缓冲区(支持指针传递或数据拷贝)
OS_MsgQPut(&p_q->MsgQ, p_msg, msg_size, opt, p_err);
p_q->MsgQty++;
// 唤醒等待该队列的任务(如有)
if (OS_PendListIsEmpty(&p_q->PendList) == DEF_NO) {
OS_PendListPost(&p_q->PendList, p_msg, msg_size, opt, p_err);
}
CPU_CRITICAL_EXIT(); // 开中断
// ...
return (1u);
}
关键特性:
- 支持
OS_OPT_POST_FIFO
(先进先出)和OS_OPT_POST_LIFO
(后进先出)模式 - 消息可通过指针传递(零拷贝,适合大数据)或拷贝(内存独立,安全但耗时)
3. 接收消息(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)
{
void *p_msg;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
// 队列有消息:直接取消息
if (p_q->MsgQty > 0u) {
p_msg = OS_MsgQGet(&p_q->MsgQ, p_msg_size, p_err);
p_q->MsgQty--;
CPU_CRITICAL_EXIT();
return (p_msg);
}
// 队列空:任务进入阻塞状态
else {
// 将当前任务加入等待链表
OS_PendListWait(&p_q->PendList,
(OS_PEND_OBJ *)p_q,
OS_TASK_PEND_ON_Q,
timeout,
opt,
p_err);
CPU_CRITICAL_EXIT();
// 任务调度(切换到其他就绪任务)
OSSched();
// 被唤醒后获取消息
p_msg = OSTCBCur->MsgPtr;
*p_msg_size = OSTCBCur->MsgSize;
// ...
return (p_msg);
}
}
阻塞机制:
- 任务调用
OSQPend()
时若队列空,会被加入PendList
并进入阻塞态 - 超时时间
timeout
为 0 表示非阻塞(立即返回错误),OS_OPT_PEND_BLOCKING
表示无限等待 - 当消息到来时,
OSQPost()
会唤醒等待队列中优先级最高的任务
4. 其他关键接口
OSQFlush()
:清空队列所有消息(重置缓冲区指针)OSQDel()
:删除队列(需确保无任务等待)OSQQuery()
:查询队列状态(当前消息数、最大容量等)