Nucleus SE队列:介绍与核心服务调用
查看RTOS Revealed系列
队列在早期文章引入。它们比邮箱提供更灵活的任务间简单消息传递手段。
使用队列
在Nucleus SE中,队列在构建时配置。应用最多可定义16个队列。若无队列配置,RTOS省略所有队列相关数据结构和API代码,保持二进制精简。
队列简单是存储槽数组,每槽足以容纳单个ADDR数据项。访问同步化使多任务可安全入队和出队消息。任务可写入队列直到所有槽满,按先进先出(FIFO)顺序从队列读取。发送到满队列或从空队列接收尝试触发错误或任务挂起,取决于API选项和Nucleus SE配置。
队列 vs 管道
Nucleus SE也支持管道,主要区别在于消息大小。队列传输单个ADDR(通常为指针);管道传输可变长度字节块;块大小配置时固定。管道将在后续文章深入探讨。
配置队列
队列数量
队列配置由nuse_config.h中#define语句控制。关键设置NUSE_QUEUE_NUMBER决定启用队列数。默认0(无队列)。可设0到16任意值;超范围值触发nuse_config_check.h产生的编译时#error。
设NUSE_QUEUE_NUMBER为非零值激活队列子系统,分配必要数据结构并启用关联API调用。
API启用
每个队列API函数在nuse_config.h有启用#define符号:
NUSE_QUEUE_SEND NUSE_QUEUE_RECEIVE NUSE_QUEUE_JAM NUSE_QUEUE_RESET NUSE_QUEUE_INFORMATION NUSE_QUEUE_COUNT 默认全设为FALSE,禁用对应服务调用。使用特定API需将其符号设为TRUE。无队列配置时启用API(除NUSE_Queue_Count()始终允许)导致编译时错误。代码中调用未启用API因实现省略导致链接时错误。
#define NUSE_QUEUE_NUMBER 0 /* 系统队列数量 – 0‑16 */ /* 服务调用启用器 */ #define NUSE_QUEUE_SEND FALSE #define NUSE_QUEUE_RECEIVE FALSE #define NUSE_QUEUE_JAM FALSE #define NUSE_QUEUE_RESET FALSE #define NUSE_QUEUE_INFORMATION FALSE #define NUSE_QUEUE_COUNT FALSE 队列服务调用
Nucleus RTOS提供十个队列相关服务调用,其中六个在Nucleus SE实现。提供以下功能:
- 发送消息 – NUSE_Queue_Send()
- 接收消息 – NUSE_Queue_Receive()
- 塞入消息到前端 – NUSE_Queue_Jam()
- 重置队列 – NUSE_Queue_Reset()
- 查询队列信息 – NUSE_Queue_Information()
- 获取配置队列数 – NUSE_Queue_Count()
- 其余调用(创建、删除、枚举、广播)在Nucleus SE不可用。
各API实现详见后续章节。
写入与读取服务
队列核心操作是写入(发送)和读取(接收)。写入队列前端称塞入。Nucleus RTOS和Nucleus SE各暴露三个基本API调用执行这些动作。
写入队列
RTOS API NU_Send_To_Queue()高度灵活,允许队列满时无限挂起或超时。Nucleus SE提供相同功能但挂起可选、不支持超时。RTOS也支持广播操作,SE无。
RTOS API: NU_Send_To_Queue()
原型:
STATUS NU_Send_To_Queue(NU_QUEUE *queue, VOID *message, UNSIGNED size, UNSIGNED suspend); 参数:
- queue – 用户提供的队列控制块指针
- message – 发送数据指针
- size – UNSIGNED元素数量;须匹配队列消息大小
- suspend – NU_NO_SUSPEND、NU_SUSPEND或超时值
返回码含NU_SUCCESS、NU_QUEUE_FULL、NU_TIMEOUT及各类无效指针或队列状态错误码。
SE API: NUSE_Queue_Send()
原型:
STATUS NUSE_Queue_Send(NUSE_QUEUE queue, ADDR *message, U8 suspend); 参数:
- queue – 队列索引(ID)
- message – 入队ADDR值指针
- suspend – NUSE_NO_SUSPEND或NUSE_SUSPEND
返回值镜像RTOS函数但反映SE有限阻塞行为。
实现概览
禁用阻塞时,例程仅检查空间并写入值或返回NUSE_QUEUE_FULL。启用阻塞时,函数循环,队列满时挂起任务直到有空间或重置发生。实现用NUSE_Queue_Blocking_Count追踪阻塞任务、NUSE_Task_Blocking_Return在槽位空出时恢复它们。
if (NUSE_Queue_Items[queue] == NUSE_Queue_Size[queue]) { return_value = NUSE_QUEUE_FULL; } else { NUSE_Queue_Data[queue][NUSE_Queue_Head[queue]++] = *message; if (NUSE_Queue_Head[queue] == NUSE_Queue_Size[queue]) NUSE_Queue_Head[queue] = 0; NUSE_Queue_Items[queue]++; return_value = NUSE_SUCCESS; } do { if (NUSE_Queue_Items[queue] == NUSE_Queue_Size[queue]) { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_QUEUE_FULL; } else { NUSE_Queue_Blocking_Count[queue]++; NUSE_Suspend_Task(NUSE_Task_Active, (queue << 4) | NUSE_QUEUE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) suspend = NUSE_NO_SUSPEND; } } else { NUSE_Queue_Data[queue][NUSE_Queue_Head[queue]++] = *message; if (NUSE_Queue_Head[queue] == NUSE_Queue_Size[queue]) NUSE_Queue_Head[queue] = 0; NUSE_Queue_Items[queue]++; if (NUSE_Queue_Blocking_Count[queue] != 0) { U8 index; NUSE_Queue_Blocking_Count[queue]--; for (index = 0; index < NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == queue)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } } while (suspend == NUSE_SUSPEND); 循环持续直到消息成功入队或任务被重置解除阻塞。实现确保线程安全访问和高效唤醒阻塞消费者。
嵌入式