4. 核间通信基础
在 VxWorks 多核 AMP 系统中,每个 CPU 核运行独立的操作系统映像或独立的任务集合,核与核之间没有共享的调度器和统一的内核锁。因此,核间通信(Inter-Processor Communication,IPC)必须由开发者显式设计。VxWorks 在 AMP 模式下最常用的两条路径是共享内存与消息队列,前者提供高带宽的数据面,后者提供带同步语义的控制面,二者通常配合使用。
共享内存机制与地址映射
共享内存的本质是一块物理地址连续、被多个核共同映射的 DDR 区域。在 AMP 中,各核的 MMU 配置相互独立,同一段物理内存可以在不同核上映射为不同的虚拟地址。例如物理基址 0x8000_0000、长度 4 MB 的共享区,在核 0 上映射为 0x8000_0000,在核 1 上映射为 0xC000_0000,两者访问的是同一份数据。VxWorks 中通常通过 sysMmuMapAdd() 在启动阶段建立该映射,并设置属性为非缓存(uncached)或写通(write-through),避免各核 L1/L2 缓存副本不一致。
共享区内部需要预先划分:头部存放控制结构(如环形缓冲区的读写指针、序号、锁标志),其后是数据区。控制结构必须使用 volatile 限定并配合内存屏障,防止编译器优化和 CPU 乱序执行破坏可见性顺序。
环形缓冲区设计
环形缓冲区(Ring Buffer)是共享内存上最常用的无锁或轻锁数据结构,适合生产者—消费者模型。其核心是三个变量:head(生产者写入位置)、tail(消费者读取位置)、size(容量,取 2 的幂便于掩码取模)。判断空的条件是 head == tail,判断满的条件是 (head + 1) & (size - 1) == tail,即牺牲一个槽位区分空与满。
单生产者单消费者(SPSC)场景下,若 head 只由生产者修改、tail 只由消费者修改,可做到无锁:生产者先写数据,再执行写屏障,最后更新 head;消费者先读 head,再读数据,最后更新 tail。多生产者或多消费者场景则必须引入自旋锁或原子操作(如 vxAtomicLib 提供的原子加/比较交换)。
typedef struct {
volatile UINT32 head; /* 生产者写指针 */
volatile UINT32 tail; /* 消费者读指针 */
UINT32 size; /* 容量,2 的幂 */
UINT8 data[SHM_BUF_SIZE]; /* 数据区 */
} RING_BUF;
/* 生产者写入(SPSC 无锁) */
status = ringPut(RING_BUF *rb, UINT8 *src, UINT32 len)
{
UINT32 h = rb->head, t = rb->tail;
UINT32 free = (t - h - 1) & (rb->size - 1);
if (len > free) return ERROR; /* 空间不足 */
/* 拷贝数据,处理回绕 */
...
VX_MEM_BARRIER(); /* 写屏障 */
rb->head = (h + len) & (rb->size - 1);
return OK;
}
核间中断(IPI)触发
共享内存只解决“数据放在哪里”,不解决“对方何时来看”。若消费者采用轮询,会浪费 CPU 并增加延迟;因此需要核间中断作为通知机制。在 ARM 多核平台上,IPI 通常通过 GIC 的 SGI(Software Generated Interrupt)实现,VxWorks 提供 vxbIpiSend() 或平台相关的 sysIpiSend() 接口,向目标核发送指定中断号。
典型用法是:生产者写完环形缓冲区并更新 head 后,调用 IPI 通知消费者核;消费者核的 IPI 中断服务程序(ISR)只做最小工作——置位信号量或唤醒任务,真正的数据搬运交给任务上下文完成,避免在 ISR 中长时间占用中断。IPI 中断号需在各核的向量表中预先注册处理函数,且要保证发送与接收的核号映射一致。
消息队列实现
VxWorks 原生 msgQCreate()/msgQSend()/msgQReceive() 是单核内的消息队列,不能跨核直接使用。AMP 下的跨核消息队列需要自行构建,常见做法是“共享内存环形缓冲区 + IPI 通知”的组合:控制消息(长度小、要求可靠)走带确认的队列,数据消息(长度大、要求带宽)走共享内存描述符传递。
一种实用结构是描述符队列:共享内存中维护一个固定长度的描述符数组,每个描述符包含数据地址、长度、类型、序号和状态标志。发送方填充描述符并置状态为 READY,然后发 IPI;接收方在 ISR 中扫描 READY 描述符,处理后置为 DONE。序号用于检测丢包和重复,状态标志的读写必须使用原子操作或内存屏障。
数据一致性保证
跨核数据一致性依赖三个层次:内存属性(共享区设为非缓存或写通,避免缓存副本)、内存屏障(VX_MEM_BARRIER() 保证写顺序对其它核可见)、同步原语(自旋锁、原子变量、序号确认)。对于超过一个总线宽度的数据,不能假设单次读写是原子的,必须用锁或双缓冲(ping-pong)切换指针的方式发布。
综合来看,一个可用的 AMP 核间通信方案应明确四件事:共享区物理地址与各核虚拟映射、环形缓冲区的容量与空满判定、IPI 中断号与 ISR 职责边界、以及描述符状态与序号的同步规则。把这四点固定下来,再叠加业务消息格式,才能得到稳定可复现的跨核通信链路。