Inter-Process Communication (IPC) Transport Layer
The RteFramework uses a OSAdapter vtable pattern for IPC transport abstraction. The application integrator chooses and implements the concrete transport (shared memory, FIFO, TCP/IP, UDP, etc.) without modifying the framework code.
The framework provides:
- Base IPC API: send(), receive(), create(), destroy() (transport-agnostic)
- Patterns: Request-Reply (RPC), Pub-Sub (broadcast)
- OSAdapter Interface: rte_osadapter_ipc_t vtable for plugging in transport
Available Transport Options
| Transport | Use Case | Latency | Range | Reliability | Setup |
| Shared Memory | Same system, high-speed | µs | Local | FIFO queue | Simple |
| FIFO (Named Pipes) | Same system, per-process | ms | Local | FIFO queue | Simple |
| TCP/IP | Distributed, reliable | ms-100ms | Network | Ordered, connection-based | Medium |
| UDP | Distributed, low-latency | ms | Network | Best-effort, connectionless | Medium |
| QNX MsgPass | RTOS environments | µs | Local | Deadlock-free | RTOS-specific |
| POSIX MQueue | POSIX systems | ms | Local | Bounded queues | POSIX |
OSAdapter Implementation Pattern
Each transport implements the OSAdapter vtable:
typedef struct rte_osadapter_ipc_s {
struct rte_ipc_impl_s * rte_ipc_handle_t
Opaque handle to a created IPC channel, returned by rte_ipc_create().
rte_status_t
Common result/status codes.
uint32_t rte_duration_ms_t
Configuration for rte_ipc_create().
OSAdapter IPC operations vtable.
At application startup, register ONE OSAdapter:
rte_status_t rte_osadapter_ipc_register(const rte_osadapter_ipc_t *adapter)
Registers the OSAdapter IPC implementation.
Shared Memory Transport
Best for:** Same-system redundancy (online/standby on same CPU)
Architecture:**
┌─────────────────────────────────────┐
│ Shared Memory Region (statically allocated in DDR)
│ ┌──────────────────────────────────┐
│ │ Channel 1: Message Queue (FIFO) │
│ └──────────────────────────────────┘
│ ┌──────────────────────────────────┐
│ │ Channel 2: Message Queue (FIFO) │
│ └──────────────────────────────────┘
│ ┌──────────────────────────────────┐
│ │ Channel 3: Message Queue (FIFO) │
│ └──────────────────────────────────┘
└─────────────────────────────────────┘
▲ ▲ ▲
│ │ │
Online Standby1 Standby2
Implementation Sketch:**
typedef struct {
uint32_t write_idx;
uint32_t read_idx;
uint8_t data[MAX_QUEUE_DEPTH][MAX_MESSAGE_SIZE];
}
Advantages:** Low latency, simple implementation, deterministic timing
Disadvantages:** Limited to same CPU/board, requires synchronization primitives
FIFO (Named Pipes) Transport
Best for:** Same-system inter-process communication (different processes)
Architecture:**
Online Process Standby Process
│ │
v v
/dev/fifo/online→standby /dev/fifo/standby→online
│ │
└─────────────┬─────────────────┘
│
(File system FIFO)
Implementation Sketch:**
typedef struct {
int fd;
char name[256];
} fifo_channel_t;
fifo_channel_t *ch = (fifo_channel_t *)handle;
ssize_t written = write(ch->fd, msg, size);
}
Advantages:** Simple, per-process isolation, OS-managed buffering
Disadvantages:** Slower than shared memory, file I/O overhead
TCP/IP Transport
Best for:** Distributed online/standby across network (LAN/WAN)
Architecture:**
Online Process (Host A)
│
v
┌─────────────────────┐
│ TCP Port 5000 │
└─────────────────────┘
│
│ TCP Connection (reliable, ordered)
│
v
┌─────────────────────┐
│ TCP Port 6000 │
└─────────────────────┘
▲
│
Standby Process (Host B)
Implementation Sketch:**
typedef struct {
int socket;
uint32_t connect_timeout;
uint32_t frame_size = size;
send(ch->socket, &frame_size, sizeof(frame_size), MSG_NOSIGNAL);
ssize_t sent = send(ch->socket, msg, size, MSG_NOSIGNAL);
}
@ RTE_STATUS_HARDWARE_FAULT
Advantages:** Works across network, reliable ordering, connection-based
Disadvantages:** Higher latency (~ms), connection management, security concerns
UDP Transport
Best for:** Low-latency distributed communication, best-effort delivery acceptable
Architecture:**
Online (Host A:5000) Standby (Host B:6000)
│ │
│ UDP Datagram (unordered) │
└──────────────────────────────>│
│<──────────────────────────────│
│ UDP Datagram (unordered) │
Implementation Sketch:**
typedef struct {
int socket;
struct sockaddr_in peer;
} udp_channel_t;
udp_channel_t *ch = (udp_channel_t *)handle;
ssize_t sent = sendto(ch->socket, msg, size, MSG_DONTWAIT,
(struct sockaddr*)&ch->peer, sizeof(ch->peer));
if (sent == -1 && errno == EAGAIN) {
}
}
Advantages:** Low latency, simple (connectionless), multicast support
Disadvantages:** Best-effort (packets can be lost), no ordering guarantee, requires vital_channel for reliability
Integration with Vital Channel
Vital Channel provides voting/redundancy ON TOP of any transport. This enables user-controlled selection and multi-transport fallback patterns.
Two-Channel Redundancy (2oo2) with Transport Choice
User decides the transports at runtime:
.name = "shm_channel",
.message_size = sizeof(train_cmd_t),
.queue_depth = 10
};
.name = "tcp_channel",
.message_size = sizeof(train_cmd_t),
.queue_depth = 5
};
.channel_timeout_ms = 1000,
.log_disagreements = true,
.backend_send = custom_backend_send,
.backend_recv = custom_backend_recv,
};
void *channels[2] = { &ch0, &ch1 };
rte_status_t rte_ipc_create(rte_ipc_storage_t *storage, const rte_ipc_config_t *config, rte_ipc_handle_t *out_handle)
Creates a bounded message channel.
rte_status_t rte_channel_init(rte_channel_storage_t *storage, const rte_channel_config_t *config)
Initializes one vital channel over a caller-supplied transport.
rte_status_t rte_channel_send(rte_channel_t *handle, const void *data, size_t data_size)
Sends data on this channel via its OSAdapter send callback.
Configuration for rte_channel_init().
Custom Callbacks for Multi-Transport Support
If you need BOTH shared memory and TCP in the same application:
rte_status_t mixed_backend_send(
void *ch,
const void *data,
size_t size) {
if (is_shm_channel(ch)) {
} else if (is_tcp_channel(ch)) {
}
}
rte_status_t mixed_backend_recv(
void *ch,
void *data,
size_t size, uint32_t timeout) {
if (is_shm_channel(ch)) {
return shm_queue_recv((
shm_queue_t*)ch, data, size, timeout);
} else if (is_tcp_channel(ch)) {
}
}
vital_cfg.backend_send = mixed_backend_send;
vital_cfg.backend_recv = mixed_backend_recv;
@ RTE_STATUS_INVALID_PARAM
Online/Standby Redundancy Patterns
Vital Channel supports several redundancy architectures:
Pattern 1: Local Dual Channels (2oo2 Same System)
Scenario:** Two processes on same CPU/board (online + standby local)
┌──────────────────────────────────┐
│ Single CPU / Single Board │
│ │
│ ┌────────┐ ┌────────┐ │
│ │ Online │ │Standby │ │
│ │Process │ │Process │ │
│ └────┬───┘ └───┬────┘ │
│ │ │ │
│ [Shared Memory Channel 0] │
│ [Shared Memory Channel 1] │
│ │ │ │
│ ┌────▼─────────────▼────┐ │
│ │ Vital Channel (2oo2) │ │
│ │ [Voting & Health] │ │
│ └───────────────────────┘ │
└──────────────────────────────────┘
Configuration:**
channel[0] = shared_memory_queue_0
channel[1] = shared_memory_queue_1
Failure Handling:**
- If channel[0] fails (e.g., standby process crashes) → Health counters show errors on ch[1] → Watchdog detects disagreement pattern → Watchdog can trigger manual intervention (not automatic safe-state)
- If channel[1] fails similarly → same pattern
- If both channels healthy but disagree → IMMEDIATE safe-state (data corruption detected)
Pattern 2: Remote Dual Channels (2oo2 Distributed)
Scenario:** Online + Standby on different CPUs/boards (over network)
Online (Host A) Standby (Host B)
│ │
v v
[Vital Compute] [Vital Verify]
│ │
├─── TCP Port 5000 ─────────>│
│<──── TCP Port 6000 ────────┤
│
[Vital Channel (2oo2)]
│
├─── TCP/IP Channel 0 ──────>│
│<───────────────────────────┤
│
├─── UDP Multicast Ch 1 ────>│ (backup, lower latency)
│<────────────────────────────┤
Configuration:**
channel[0] = tcp_channel (high reliability, ~50ms latency)
channel[1] = udp_channel (low latency ~5ms, best-effort)
If tcp_recv times out:
→ Use udp result (if available)
→ If UDP also times out → INSUFFICIENT_QUORUM
If tcp and udp disagree:
→ VOTING_DISAGREED → SAFE-STATE
Pattern 3: Triple Redundancy (2oo3 Distributed)
Scenario:** Three redundant processes (tolerate 1 Byzantine fault)
Process A Process B Process C
(Online) (Standby 1) (Standby 2)
│ │ │
├──────────────TCP───>│ │
│<───────────────────TCP─────────────────>│
│ │<────TCP────────────┤
│<────────UDP─────────┤ │
│ │<───────UDP────────>│
│<──────────────UDP──────────────────────>│
│
[Vital Channel 2oo3]
├─ Channel 0: TCP to B (reliable)
├─ Channel 1: UDP to C (fast)
└─ Channel 2: TCP to C (reliable)
Voting:
- 2 or 3 channels agree → AGREED (that's the result)
- 0/1 channels return (others timeout) → INSUFFICIENT_QUORUM
- Each pair gives different result → DISAGREED → SAFE-STATE
Pattern 4: Hybrid Transports (Speed + Reliability)
Scenario:** Use fastest transport (shared memory) + fallback to reliable transport (TCP)
Online Process (Host A)
│
├─── [Shared Memory Channel 0] ───────┐
│ (0.1ms, local standby) │
│ v
│ ┌──────────────────┐
│ │ Standby (Host B) │
│ │ (Fast response) │
│ └──────────────────┘
│
├─── [TCP/IP Channel 1] ───────────────┐
│ (50ms, remote standby) │
│ v
│ ┌──────────────────┐
│ │ Remote Standby │
│ │ (Reliable backup)│
│ └──────────────────┘
│
[Vital Channel 2oo2]
│
└─ Normal: Both channels agree (data replicated to both)
Safe-State: Channels disagree (data corruption) OR timeout
Transport Selection Decision Tree
Is communication local (same CPU/board)?
├─ YES → Use SHARED MEMORY
│ Pros: <1µs latency, deterministic, simple locking
│ Cons: Limited to local system
│
└─ NO → Is reliability more important than latency?
├─ YES → Use TCP/IP
│ Pros: Guaranteed ordered delivery, connection-aware
│ Cons: ~10-50ms latency, connection overhead
│
└─ NO → Use UDP
Pros: <5ms latency, simple (connectionless)
Cons: Best-effort, packets can be lost
→ MITIGATE: Use vital_channel voting for reliability
Recommended Configurations
For Railway ERTMS (SIL 4)
config = {
.channel_timeout_ms = 500,
.log_disagreements = true,
.backend_send = tcp_send,
.backend_recv = tcp_recv,
};
For Automotive (ASIL D)
config = {
.channel_timeout_ms = 100,
.log_disagreements = true,
.backend_send = hybrid_send,
.backend_recv = hybrid_recv,
};
For High-Availability Web Service
config = {
.channel_timeout_ms = 2000,
.log_disagreements = false,
.backend_send = tcp_send,
.backend_recv = tcp_recv,
};
Implementation Checklist
- Choose transport for your use case (shared memory vs network)
- Decide redundancy pattern (2oo2 vs 2oo3 vs NMR)
- Implement OSAdapter callbacks (backend_send/backend_recv)
- Create transport-specific channels (2 or 3)
- Configure vital_channel with callbacks and channels
- Use rte_channel_send/receive for voting communication
- Implement watchdog for health-based channel isolation
- Test disagreement scenarios (simulate channel failures)
- Verify safe-state triggers on voting failure