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);
}

运行与验证

  1. 编译集成:将上述代码放入分区模块的C源文件中,与ARINC 653 APEX库链接,生成分区可执行映像。
  2. 系统集成:在系统集成文件中为该分区分配至少一个队列端口资源(端口名需与代码中"SENDER_Q"一致),并配置分区入口为PartitionMain。
  3. 运行观察:加载系统映像并启动。接收进程每成功接收并校验一条消息,会通过printf输出一行Receiver: msg N OK。
  4. 通过标准:控制台依次出现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。
  • 发送进程优先级高于接收进程,确保发送先执行、消息入队后接收方再读取,避免因调度顺序导致接收方先运行时无消息可收而阻塞。