4. 使用进程间通信队列
任务目标
在ARINC 653分区内创建两个进程,通过内核提供的队列(Queueing Port)服务实现单向消息传递:发送进程将一组结构化消息写入队列,接收进程从队列中读取并校验消息内容,以此验证分区内进程间通信(IPC)的完整链路。
环境与依赖
- 操作系统:VxWorks 653 或符合ARINC 653标准的RTOS(如PikeOS、LynxSecure)
- 语言:C(ARINC 653 APEX接口)
- 必要头文件:
apos.h(或arinc653.h),提供CREATE_QUEUING_PORT、SEND_QUEUING_MESSAGE、RECEIVE_QUEUING_MESSAGE等APEX服务
实现方案
本案例使用ARINC 653标准的队列端口(Queuing Port)实现分区内进程间通信。队列端口是APEX定义的通信对象,支持消息的先进先出传递,并具备消息长度与深度属性。发送进程与接收进程通过端口ID访问同一队列,无需共享内存或显式同步。
关键完整代码
#include <apos.h>
#include <string.h>
#include <stdio.h>
#define QUEUE_DEPTH 4
#define MESSAGE_SIZE 16
#define SENDER_PRIORITY 10
#define RECEIVER_PRIORITY 20
typedef struct {
UINT32 seq;
UINT32 payload[3];
} TEST_MESSAGE;
static QUEUING_PORT_ID_TYPE qport_id;
/* 发送进程主函数 */
static void SenderProcess(void) {
TEST_MESSAGE msg;
RETURN_CODE_TYPE ret;
UINT32 i;
for (i = 0; i < 3; i++) {
msg.seq = i + 1;
msg.payload[0] = 0x1111 * (i + 1);
msg.payload[1] = 0x2222 * (i + 1);
msg.payload[2] = 0x3333 * (i + 1);
SEND_QUEUING_MESSAGE(qport_id, (MESSAGE_ADDR_TYPE)&msg, MESSAGE_SIZE, 0, &ret);
if (ret != NO_ERROR) {
printf("Sender: send failed, ret=%d\n", (int)ret);
break;
}
}
}
/* 接收进程主函数 */
static void ReceiverProcess(void) {
TEST_MESSAGE msg;
RETURN_CODE_TYPE ret;
MESSAGE_SIZE_TYPE actual_len;
UINT32 i;
for (i = 0; i < 3; i++) {
RECEIVE_QUEUING_MESSAGE(qport_id, (MESSAGE_ADDR_TYPE)&msg, MESSAGE_SIZE, &actual_len, &ret);
if (ret != NO_ERROR) {
printf("Receiver: recv failed, ret=%d\n", (int)ret);
break;
}
if (actual_len != MESSAGE_SIZE) {
printf("Receiver: length mismatch, got %d\n", (int)actual_len);
break;
}
/* 校验消息内容 */
if (msg.seq != i + 1 || msg.payload[0] != 0x1111 * (i + 1) ||
msg.payload[1] != 0x2222 * (i + 1) || msg.payload[2] != 0x3333 * (i + 1)) {
printf("Receiver: data mismatch at seq=%d\n", (int)msg.seq);
break;
}
printf("Receiver: msg %d OK\n", (int)msg.seq);
}
}
/* 分区初始化入口 */
void PartitionMain(void) {
PROCESS_ID_TYPE sender_id, receiver_id;
PROCESS_ATTRIBUTE_TYPE attr;
RETURN_CODE_TYPE ret;
/* 创建队列端口:单向,发送方向 */
CREATE_QUEUING_PORT("SENDER_Q", MESSAGE_SIZE, QUEUE_DEPTH, DESTINATION, &qport_id, &ret);
if (ret != NO_ERROR) {
printf("Create qport failed, ret=%d\n", (int)ret);
return;
}
/* 创建发送进程 */
attr.PERIOD = INFINITE_TIME_PERIOD;
attr.TIME_CAPACITY = INFINITE_TIME_CAPACITY;
attr.ENTRY_POINT = (SYSTEM_ADDRESS_TYPE)SenderProcess;
attr.BASE_PRIORITY = SENDER_PRIORITY;
attr.STACK_SIZE = 4096;
attr.DEADLINE = SOFT;
CREATE_PROCESS(&attr, &sender_id, &ret);
if (ret != NO_ERROR) {
printf("Create sender failed, ret=%d\n", (int)ret);
return;
}
/* 创建接收进程 */
attr.ENTRY_POINT = (SYSTEM_ADDRESS_TYPE)ReceiverProcess;
attr.BASE_PRIORITY = RECEIVER_PRIORITY;
CREATE_PROCESS(&attr, &receiver_id, &ret);
if (ret != NO_ERROR) {
printf("Create receiver failed, ret=%d\n", (int)ret);
return;
}
START(sender_id, &ret);
START(receiver_id, &ret);
}
运行与验证
- 编译集成:将上述代码放入分区模块的C源文件中,与ARINC 653 APEX库链接,生成分区可执行映像。
- 系统集成:在系统集成文件中为该分区分配至少一个队列端口资源(端口名需与代码中
"SENDER_Q"一致),并配置分区入口为PartitionMain。 - 运行观察:加载系统映像并启动。接收进程每成功接收并校验一条消息,会通过
printf输出一行Receiver: msg N OK。 - 通过标准:控制台依次出现
Receiver: msg 1 OK、Receiver: msg 2 OK、Receiver: msg 3 OK,且无任何错误打印,即表示队列通信功能正确。若出现send failed或recv failed,检查端口创建参数与系统配置中的队列资源是否匹配。
关键点说明
- 队列端口创建时方向参数为
DESTINATION,表示该端口仅用于发送;接收方使用同一端口ID调用接收服务,ARINC 653内核自动完成消息路由。 - 消息长度固定为
MESSAGE_SIZE(16字节),发送与接收必须使用相同长度,否则接收服务返回INVALID_MESSAGE_SIZE。 - 发送进程优先级高于接收进程,确保发送先执行、消息入队后接收方再读取,避免因调度顺序导致接收方先运行时无消息可收而阻塞。