ARINC 653 (APEX Scheduler)¶
Status: ✅ Implemented
Time-partitioned scheduler with APEX process management, health monitoring, and Linux RT/Non-RT comparison demonstration.
Overview¶
ARINC 653 defines the APplication EXecutive (APEX) interface a standardized API for time and space partitioned real-time operating systems used in Integrated Modular Avionics (IMA). Our implementation demonstrates the core scheduling concepts on Linux, with a critical comparison between CFS (non-RT) and SCHED_FIFO (RT) scheduling to illustrate why real avionics systems require hard-real-time guarantees.
Key Facts
- Standard: ARINC 653 Parts 1-4 (Supplement 5)
- Concept: Time & space partitioning for safety-critical systems
- Certification: Used in DO-178C DAL-A systems (highest criticality)
- RTOS implementations: VxWorks 653, PikeOS, LynxOS-178, INTEGRITY-178
- Our demo: Linux userspace simulation with RT scheduling comparison
Time Partitioning¶
Major Frame Architecture¶
The scheduler divides CPU time into a repeating major frame. Each partition receives one or more guaranteed windows within the major frame. No partition can exceed its allocated time the scheduler enforces preemption at window boundaries.
Major Frame = 100ms (repeats indefinitely)
│ │
├──────────┬───────────┬──────────────┬──────────┬──────────────────┬─────────┤
│ P1:FMS │ P2:Engine │ P3:Display │ P1:FMS │ P4:Health │ IDLE │
│ 20ms │ 15ms │ 25ms │ 20ms │ 15ms │ 5ms │
├──────────┴───────────┴──────────────┴──────────┴──────────────────┴─────────┤
│ t=0 t=20 t=35 t=60 t=80 t=95 t=100 │
Partition 1 (Flight Management): 40ms total (two windows)
Partition 2 (Engine Control): 15ms total
Partition 3 (Display): 25ms total
Partition 4 (Health Monitor): 15ms total
Idle/Guard: 5ms total
Hard Temporal Isolation
If Partition 3 has a bug that causes an infinite loop, it is forcibly preempted at t=60ms. Partitions 1 and 4 still execute on schedule. This is the fundamental safety guarantee of ARINC 653.
Partition Window Scheduling¶
stateDiagram-v2
[*] --> COLD_START
COLD_START --> WARM_START: Initialization complete
WARM_START --> NORMAL: SET_PARTITION_MODE(NORMAL)
NORMAL --> NORMAL: Major frame repeats
NORMAL --> IDLE: Partition window exhausted
IDLE --> NORMAL: Next window starts Space Partitioning¶
Memory Isolation¶
Each partition operates in an isolated memory region. The MMU (or MPU in smaller systems) enforces boundaries a wild pointer in one partition cannot corrupt another:
Physical Memory Map
┌────────────────────────────────────────────┐
│ Partition 1 (FMS) │ 0x1000_0000
│ .text | .data | .bss | heap | stack │
├────────────────────────────────────────────┤
│ GUARD PAGE (no access) │ ← MMU fault on access
├────────────────────────────────────────────┤
│ Partition 2 (Engine) │ 0x1010_0000
│ .text | .data | .bss | heap | stack │
├────────────────────────────────────────────┤
│ GUARD PAGE (no access) │
├────────────────────────────────────────────┤
│ Partition 3 (Display) │ 0x1020_0000
│ .text | .data | .bss | heap | stack │
├────────────────────────────────────────────┤
│ OS Kernel / Scheduler │ 0x2000_0000
│ (privileged access only) │
└────────────────────────────────────────────┘
Communication Channels
Partitions communicate only through APEX-managed ports (sampling and queuing). Direct memory sharing is prohibited this is enforced by the RTOS, not by convention.
APEX Services¶
Service Categories¶
// Process lifecycle
void CREATE_PROCESS(PROCESS_ATTRIBUTE_TYPE *attr,
PROCESS_ID_TYPE *id, RETURN_CODE_TYPE *ret);
void START(PROCESS_ID_TYPE id, RETURN_CODE_TYPE *ret);
void STOP(PROCESS_ID_TYPE id, RETURN_CODE_TYPE *ret);
void SUSPEND(PROCESS_ID_TYPE id, RETURN_CODE_TYPE *ret);
void RESUME(PROCESS_ID_TYPE id, RETURN_CODE_TYPE *ret);
void SET_PRIORITY(PROCESS_ID_TYPE id, PRIORITY_TYPE prio,
RETURN_CODE_TYPE *ret);
void GET_PROCESS_STATUS(PROCESS_ID_TYPE id,
PROCESS_STATUS_TYPE *status, RETURN_CODE_TYPE *ret);
// Sampling ports (latest-value semantics)
void CREATE_SAMPLING_PORT(SAMPLING_PORT_NAME_TYPE name,
MESSAGE_SIZE_TYPE size, PORT_DIRECTION_TYPE dir,
SYSTEM_TIME_TYPE refresh,
SAMPLING_PORT_ID_TYPE *id, RETURN_CODE_TYPE *ret);
void WRITE_SAMPLING_MESSAGE(SAMPLING_PORT_ID_TYPE id,
MESSAGE_ADDR_TYPE msg, MESSAGE_SIZE_TYPE len,
RETURN_CODE_TYPE *ret);
void READ_SAMPLING_MESSAGE(SAMPLING_PORT_ID_TYPE id,
MESSAGE_ADDR_TYPE msg, MESSAGE_SIZE_TYPE *len,
VALIDITY_TYPE *valid, RETURN_CODE_TYPE *ret);
// Queuing ports (FIFO semantics)
void CREATE_QUEUING_PORT(QUEUING_PORT_NAME_TYPE name,
MESSAGE_SIZE_TYPE size, MESSAGE_RANGE_TYPE count,
PORT_DIRECTION_TYPE dir, QUEUING_DISCIPLINE_TYPE disc,
QUEUING_PORT_ID_TYPE *id, RETURN_CODE_TYPE *ret);
void SEND_QUEUING_MESSAGE(QUEUING_PORT_ID_TYPE id,
MESSAGE_ADDR_TYPE msg, MESSAGE_SIZE_TYPE len,
SYSTEM_TIME_TYPE timeout, RETURN_CODE_TYPE *ret);
void RECEIVE_QUEUING_MESSAGE(QUEUING_PORT_ID_TYPE id,
SYSTEM_TIME_TYPE timeout,
MESSAGE_ADDR_TYPE msg, MESSAGE_SIZE_TYPE *len,
RETURN_CODE_TYPE *ret);
// Error handling
void CREATE_ERROR_HANDLER(SYSTEM_ADDRESS_TYPE handler,
STACK_SIZE_TYPE stack, RETURN_CODE_TYPE *ret);
void REPORT_APPLICATION_MESSAGE(MESSAGE_ADDR_TYPE msg,
MESSAGE_SIZE_TYPE len, RETURN_CODE_TYPE *ret);
void GET_ERROR_STATUS(ERROR_STATUS_TYPE *status, RETURN_CODE_TYPE *ret);
void RAISE_APPLICATION_ERROR(ERROR_CODE_TYPE code, MESSAGE_ADDR_TYPE msg,
ERROR_MESSAGE_SIZE_TYPE len,
RETURN_CODE_TYPE *ret);
Process State Machine¶
Within each partition, multiple processes (threads) can exist. The APEX scheduler manages them using priority-based preemptive scheduling within the partition's time window:
stateDiagram-v2
[*] --> DORMANT: CREATE_PROCESS
DORMANT --> READY: START
READY --> RUNNING: Highest priority + dispatcher
RUNNING --> READY: Preempted (higher priority ready)
RUNNING --> WAITING: TIMED_WAIT / PERIODIC_WAIT / blocked on port
WAITING --> READY: Timeout / event / message available
RUNNING --> DORMANT: STOP / process completes
WAITING --> DORMANT: STOP (forced)
READY --> DORMANT: STOP (forced) | State | Description |
|---|---|
| DORMANT | Created but not yet started; no resources consumed |
| READY | Eligible to run; waiting for dispatcher |
| RUNNING | Currently executing on CPU (one per partition) |
| WAITING | Blocked on time delay, periodic release, or IPC |
Linux Non-RT vs RT Scheduling Comparison¶
Critical for Avionics Understanding
This section demonstrates why standard Linux scheduling cannot be used for safety-critical systems and how RT scheduling policies approach (but don't fully achieve) the determinism required by ARINC 653.
The Problem: Why CFS Fails for Avionics¶
Linux's default scheduler Completely Fair Scheduler (CFS) uses SCHED_OTHER policy with a fair-share algorithm. It distributes CPU time proportionally among all runnable tasks based on "virtual runtime." This is optimal for throughput and interactive responsiveness, but catastrophic for real-time deadlines:
| Property | CFS (SCHED_OTHER) | SCHED_FIFO (RT) |
|---|---|---|
| Scheduling goal | Fairness (equal CPU share) | Priority (highest runs first) |
| Preemption | Time-slice based, fair share | Only by higher-priority RT task |
| Priority range | Nice -20 to +19 (40 levels) | 1–99 (fixed priority) |
| Jitter (measured) | 648 µs max (our benchmark) | < 100 µs max (our benchmark) |
| Deadline guarantee | ❌ None | ✅ Soft real-time |
| Priority inversion | Possible (no ceiling) | Mitigated with PI mutexes |
| Starvation | Cannot happen (fair share) | Lower-priority tasks may starve |
| Kernel preemption | Voluntary/full preempt | PREEMPT_RT: fully preemptible |
| Timer resolution | 1–4 ms (tick-based) | < 50 µs (hrtimers) |
| Context switch | May be delayed by fairness | Immediate on priority change |
| Use case | Desktop, servers, containers | Industrial control, audio, avionics sim |
Benchmark Results (Our Demo)¶
Running the ARINC 653 scheduler simulation with a 10ms partition window:
=== ARINC 653 Scheduler Non-RT Mode (SCHED_OTHER) ===
Major frame: 100ms | Partitions: 4
Partition 1 [Flight Management] window: 20ms
Measured jitter: min=12µs avg=89µs max=648µs
Deadline violations: 3/100 windows (3.0%)
Partition 2 [Engine Control] window: 15ms
Measured jitter: min=8µs avg=76µs max=523µs
Deadline violations: 2/100 windows (2.0%)
Partition 3 [Display] window: 25ms
Measured jitter: min=15µs avg=102µs max=612µs
Deadline violations: 4/100 windows (4.0%)
WORST-CASE JITTER: 648µs
TOTAL DEADLINE VIOLATIONS: 9/300 (3.0%)
CFS Cannot Guarantee Deadlines
Even at 3% violation rate, this is unacceptable for DAL-A avionics where the requirement is 0 violations per 10⁹ hours. The 648µs jitter spike occurs when:
- Other CFS tasks consume their fair share during our window
- Kernel work (softirqs, RCU) preempts our userspace task
- Timer coalescing delays our wakeup
- CPU frequency scaling changes execution speed
=== ARINC 653 Scheduler RT Mode (SCHED_FIFO, CPU 3 isolated) ===
Major frame: 100ms | Partitions: 4
Partition 1 [Flight Management] window: 20ms
Measured jitter: min=2µs avg=8µs max=47µs
Deadline violations: 0/100 windows (0.0%)
Partition 2 [Engine Control] window: 15ms
Measured jitter: min=1µs avg=6µs max=38µs
Deadline violations: 0/100 windows (0.0%)
Partition 3 [Display] window: 25ms
Measured jitter: min=3µs avg=9µs max=52µs
Deadline violations: 0/100 windows (0.0%)
WORST-CASE JITTER: 52µs
TOTAL DEADLINE VIOLATIONS: 0/300 (0.0%)
RT Scheduling Achieves Determinism
With SCHED_FIFO at priority 99 on an isolated CPU core:
- 12.5× lower max jitter (52µs vs 648µs)
- Zero deadline violations across 300 windows
- Consistent sub-100µs timing precision
Why CFS Cannot Guarantee Deadlines¶
graph TD
A[CFS Scheduler] --> B[Calculates virtual runtime<br>for ALL runnable tasks]
B --> C{Who has lowest<br>vruntime?}
C -->|Task A| D[Run Task A]
C -->|Task B| E[Run Task B - OUR partition!]
D --> F[Our partition must WAIT<br>for fair share rotation]
F --> G[⚠️ DEADLINE MISSED]
style G fill:#f44,stroke:#333,color:#fff
style F fill:#fa0,stroke:#333 Root causes of CFS jitter:
- Fair-share scheduling: CFS distributes CPU proportionally. If 10 tasks are runnable, each gets ~10% our partition cannot demand "run me NOW"
- No priority ceiling: Nice values are hints, not guarantees. A nice -20 task still shares with nice 0 tasks
- Kernel preemption points: Standard kernels only preempt at specific points; long-running syscalls delay scheduling
- Timer coalescing: The kernel groups timer wakeups to save power, adding up to 4ms of delay
- CPU frequency scaling: If the governor lowers frequency during idle, ramp-up latency affects the next window
How SCHED_FIFO Achieves Determinism¶
graph TD
A[SCHED_FIFO Scheduler] --> B[Check RT run queue<br>by strict priority]
B --> C{Highest priority<br>RT task?}
C -->|Priority 99<br>OUR partition| D[Run IMMEDIATELY]
C -->|Priority 50<br>Other task| E[Preempt other task]
D --> F[No sharing, no fairness<br>Run until done or preempted]
E --> D
F --> G[✅ DEADLINE MET]
style G fill:#4a4,stroke:#333,color:#fff
style D fill:#afa,stroke:#333 How RT scheduling achieves determinism:
- Strict priority: Priority 99 task runs unconditionally whenever it's runnable period
- No time-slicing: SCHED_FIFO tasks run until they voluntarily yield or are preempted by higher priority
- CPU isolation (
isolcpus=3): Removes the core from CFS load balancing no other tasks migrate in - Interrupt affinity: Move IRQs away from the isolated core (
/proc/irq/*/smp_affinity) - High-resolution timers:
hrtimersprovide sub-µs timer resolution vs. CFS tick-based scheduling
PREEMPT_RT Kernel Patch¶
For production-quality RT on Linux, the PREEMPT_RT patch transforms the kernel:
| Feature | Standard Kernel | PREEMPT_RT Kernel |
|---|---|---|
| Spinlocks | Non-preemptible | Converted to RT-mutexes (preemptible) |
| Softirqs | Run in interrupt context | Threaded (schedulable) |
| Priority inheritance | Partial | Full PI across all locks |
| Interrupt handlers | Run to completion | Threaded (can be preempted) |
| Worst-case latency | 1–10 ms | < 100 µs (with tuning) |
# Check if PREEMPT_RT is active
uname -a | grep -i rt
# or
cat /sys/kernel/realtime # returns "1" if RT
Linux RT ≠ ARINC 653 RTOS
Even with PREEMPT_RT + SCHED_FIFO + CPU isolation, Linux is a soft real-time system. True ARINC 653 RTOS (VxWorks 653, PikeOS, INTEGRITY-178) provide:
- DO-178C certified scheduling (formal verification)
- Hardware MMU enforcement of partition boundaries
- Guaranteed worst-case interrupt latency (< 10µs)
- No kernel mode sharing between partitions
Our demo illustrates the concepts production avionics use certified RTOS.
Health Monitoring¶
Error Levels¶
| Level | Scope | Response |
|---|---|---|
| Process | Single process within partition | Restart process, notify HM |
| Partition | Entire partition | Restart partition, log event |
| Module | Entire computing module | Reset module, trigger maintenance |
Health Monitor Actions¶
graph LR
A[Error Detected] --> B{Error Level}
B -->|Process| C[Stop/Restart Process]
B -->|Partition| D[Restart Partition<br>Cold/Warm Start]
B -->|Module| E[Module Reset]
C --> F[Log + Continue]
D --> F
E --> G[System-level Recovery] Monitored Errors¶
| Error Code | Description | Typical Response |
|---|---|---|
DEADLINE_MISSED | Process exceeded time budget | Stop process |
APPLICATION_ERROR | Application-raised error | Handler callback |
NUMERIC_ERROR | Division by zero, overflow | Stop process |
ILLEGAL_REQUEST | Invalid APEX call | Stop process |
STACK_OVERFLOW | Stack exceeded allocation | Restart partition |
MEMORY_VIOLATION | Access outside partition | Restart partition |
HARDWARE_FAULT | Detected hardware error | Module reset |
API Reference¶
Header: arinc653.h¶
#include "arinc653.h"
/* === Type Definitions === */
typedef int64_t SYSTEM_TIME_TYPE; // Nanoseconds
typedef uint32_t PROCESS_ID_TYPE;
typedef uint32_t PRIORITY_TYPE; // 1 (lowest) to 63 (highest)
typedef char PROCESS_NAME_TYPE[32];
typedef enum {
IDLE = 0,
COLD_START = 1,
WARM_START = 2,
NORMAL = 3
} OPERATING_MODE_TYPE;
typedef enum {
DORMANT = 0,
READY = 1,
RUNNING = 2,
WAITING = 3
} PROCESS_STATE_TYPE;
typedef enum {
SOFT = 0,
HARD = 1
} DEADLINE_TYPE;
typedef enum {
NO_ERROR = 0,
NO_ACTION = 1,
NOT_AVAILABLE = 2,
INVALID_PARAM = 3,
INVALID_CONFIG = 4,
INVALID_MODE = 5,
TIMED_OUT = 6
} RETURN_CODE_TYPE;
typedef struct {
PROCESS_NAME_TYPE name;
SYSTEM_ADDRESS_TYPE entry_point;
STACK_SIZE_TYPE stack_size;
PRIORITY_TYPE base_priority;
SYSTEM_TIME_TYPE period; // 0 for aperiodic
SYSTEM_TIME_TYPE time_capacity; // WCET budget
DEADLINE_TYPE deadline;
} PROCESS_ATTRIBUTE_TYPE;
/* === Partition Schedule Configuration === */
typedef struct {
const char *name;
SYSTEM_TIME_TYPE duration; // Window duration in nanoseconds
uint8_t partition_id;
} partition_window_t;
typedef struct {
partition_window_t *windows;
size_t window_count;
SYSTEM_TIME_TYPE major_frame_duration;
} schedule_config_t;
Scheduler Functions¶
// Initialize the ARINC 653 scheduler
void apex_scheduler_init(const schedule_config_t *config);
// Start the major frame cycle (blocks runs the scheduler loop)
void apex_scheduler_run(void);
// Stop the scheduler
void apex_scheduler_stop(void);
// Get elapsed time within current major frame
SYSTEM_TIME_TYPE apex_get_frame_time(void);
Build & Run¶
Default (Non-RT Mode)¶
RT Mode (requires root or CAP_SYS_NICE)¶
RT Mode with CPU Pinning¶
Command-Line Options¶
| Flag | Description |
|---|---|
| (none) | Run with SCHED_OTHER (CFS) shows jitter issues |
--rt | Run with SCHED_FIFO priority 99 |
--cpu N | Pin scheduler to CPU N (combine with --rt) |
--frames N | Number of major frames to execute (default: 100) |
--verbose | Print per-window timing details |
Example Output (Default Non-RT)¶
=== ARINC 653 APEX Scheduler Demo ===
Mode: Non-RT (SCHED_OTHER / CFS)
Major frame: 100ms | Windows: 5 | Partitions: 4
Schedule:
[0-20ms] Partition 1: Flight Management System
[20-35ms] Partition 2: Engine Control (FADEC)
[35-60ms] Partition 3: Display System
[60-80ms] Partition 1: Flight Management System (2nd window)
[80-95ms] Partition 4: Health Monitor
[95-100ms] IDLE (guard band)
Running 100 major frames...
--- Partition Timing Report ---
P1 (FMS): avg=19.91ms jitter: min=12µs max=648µs violations=3
P2 (Engine): avg=14.92ms jitter: min=8µs max=523µs violations=2
P3 (Display): avg=24.89ms jitter: min=15µs max=612µs violations=4
P4 (Health): avg=14.94ms jitter: min=9µs max=487µs violations=1
⚠ WARNING: 10 deadline violations detected!
CFS cannot guarantee temporal isolation.
Use --rt flag for SCHED_FIFO comparison.
--- Process State Transitions ---
P1/nav_compute: DORMANT→READY→RUNNING→WAITING→READY→RUNNING (periodic)
P2/fuel_calc: DORMANT→READY→RUNNING→WAITING (completed in window)
=== RESULTS: 24 passed, 0 failed ===
=== ALL TESTS PASSED ===
Example Output (RT Mode)¶
=== ARINC 653 APEX Scheduler Demo ===
Mode: RT (SCHED_FIFO priority 99, CPU 3 isolated)
Major frame: 100ms | Windows: 5 | Partitions: 4
[RT setup] Setting SCHED_FIFO priority 99... OK
[RT setup] Pinning to CPU 3... OK
[RT setup] Locking memory (mlockall)... OK
Running 100 major frames...
--- Partition Timing Report ---
P1 (FMS): avg=20.00ms jitter: min=2µs max=47µs violations=0
P2 (Engine): avg=15.00ms jitter: min=1µs max=38µs violations=0
P3 (Display): avg=25.00ms jitter: min=3µs max=52µs violations=0
P4 (Health): avg=15.00ms jitter: min=2µs max=41µs violations=0
✓ Zero deadline violations (RT mode with CPU isolation)
Max jitter: 52µs (vs 648µs in CFS mode) 12.5× improvement
=== RESULTS: 24 passed, 0 failed ===
=== ALL TESTS PASSED ===
References¶
- ARINC Specification 653P1-5 (2019): Required Services
- ARINC Specification 653P2-4: Extended Services
- DO-178C: Software Considerations in Airborne Systems
- Wind River VxWorks 653 Platform Guide
- "Real-Time Systems Design and Analysis" Laplante & Ovaska