RTOS가 필요한 이유 — 일반 OS와의 결정적 차이
#한 줄 요약
“deadline이 있으면 RTOS입니다.” 슈퍼루프는 빠른 작업과 느린 작업이 직렬화되어 실시간성이 깨집니다.
#시작 — 가장 단순한 임베디드 프로그램
int main(void) { init_hw(); while (1) { read_sensors(); // 1 ms compute_pid(); // 0.5 ms update_actuators(); // 0.3 ms log_uart(); // 5 ms ← 느림 }}이 “Super-Loop” 패턴은 다음과 같은 장점이 있습니다.
- 구조가 단순합니다.
- 디버그가 쉽습니다 (실행 순서가 코드 그대로입니다).
- 오버헤드가 0입니다.
작은 MCU 프로젝트라면 이걸로 충분합니다.
#한계 — 가장 느린 작업이 전체를 결정합니다
위 예에서 한 사이클은 1 + 0.5 + 0.3 + 5 = 6.8 ms입니다.
만약 PID 제어가 1 kHz (1 ms마다) 필요하다면 어떨까요? 슈퍼루프에서 PID는 6.8 ms마다 실행됩니다. 제어가 불가능합니다.
해결책 후보:
#(1) UART 로깅을 비동기로
while (1) { read_sensors(); compute_pid(); update_actuators(); if (uart_can_send()) log_uart_nonblocking();}일부는 해결되지만 모든 작업을 비동기로 재작성해야 합니다. 코드 복잡도가 폭증합니다.
#(2) 인터럽트로 빠른 작업 분리
void TIM1_IRQHandler(void) { read_sensors(); compute_pid(); update_actuators();}인터럽트 우선순위로 PID를 분리할 수 있습니다. 하지만 복잡한 작업을 ISR에서 처리하면 ISR이 길어져 다른 인터럽트가 막힙니다.
#(3) RTOS
void pid_task(void *arg) { while (1) { read_sensors(); compute_pid(); update_actuators(); vTaskDelay(1); // 1 ms 정확 주기 }}
void log_task(void *arg) { while (1) { log_uart(); // 5 ms — 다른 task에 영향 없음 }}RTOS가 우선순위와 preemption을 관리합니다. PID는 정확히 1 ms 주기로 실행되고, log는 남는 시간에만 실행됩니다.
#RTOS의 3가지 핵심 가치
#1. Preemption — 강제 전환
저우선 task가 실행 중이라도 고우선 task가 ready 되면 즉시 전환됩니다. 슈퍼루프의 모든 일이 끝나기 전엔 다음 일을 못 하는 문제를 해결합니다.
#2. 우선순위 (Priority)
각 task에 0-31 (또는 0-255) 우선순위를 부여합니다. 높은 게 항상 먼저 실행됩니다.
#3. 동기화 프리미티브
- Mutex — 공유 자원 보호에 사용합니다.
- Semaphore — task 간 신호를 전달합니다.
- Queue — 메시지를 전달합니다.
- Event group — 다중 조건을 대기합니다.
ISR과 task, task와 task 사이에서 안전한 통신을 제공합니다.
#실시간성 — Hard vs Soft
| Soft Real-Time | Hard Real-Time | |
|---|---|---|
| Deadline miss 결과 | 품질 저하 | 시스템 실패 |
| 예 | 비디오 frame drop, 게임 FPS | 자동차 ESC, 인공 호흡기, ABS |
| 확률 | 99% 충분 | 100% 필요 |
| OS 요구 | Linux로도 가능 | RTOS 또는 bare-metal |
RTOS는 hard real-time을 보장합니다. 수학적으로 deadline 만족을 증명할 수 있습니다.
#RTOS는 Linux와 다르다
| RTOS (FreeRTOS·Zephyr·VxWorks) | Linux | |
|---|---|---|
| 타깃 | MCU (수 KB ~ 수 MB) | SoC (수십 MB+) |
| Footprint | 5-50 KB | 5-50 MB |
| Boot time | 수 ms | 수 초 |
| Scheduler latency | < 10 µs | 보통 < 1 ms (PREEMPT_RT < 100 µs) |
| Determinism | High | Best-effort |
| MMU 요구 | 없음 (MPU 선택) | 필수 |
| Driver 모델 | 단순 | 복잡 (devicetree, sysfs, etc.) |
Linux도 PREEMPT_RT로 RTOS-like가 가능합니다. 2024년 9월 Linux 6.12에 mainline merge되어 더 이상 별도 패치가 아닙니다. 다만 진짜 µs-deterministic은 여전히 RTOS 영역입니다.
#언제 RTOS를 안 써야 하나
- 단일 task만 있는 단순 시스템입니다 (LED 깜빡임, 단순 센서 read 등).
- deadline이 매우 느슨합니다 (≥ 100 ms).
- 메모리가 매우 작습니다 (≤ 4 KB SRAM).
- 인증·검증 비용이 RTOS 도입 가치를 넘는 경우입니다.
이런 경우엔 bare-metal + interrupt-driven이 더 깔끔합니다.
#흔한 RTOS 4종
| RTOS | 특징 | 라이선스 | 대표 사용 |
|---|---|---|---|
| FreeRTOS | 가장 널리 사용, 단순 | MIT | AWS IoT, Arduino |
| Zephyr | Linux Foundation, 풍부한 driver | Apache 2.0 | Nordic, Intel, NXP |
| RT-Thread | 경량, 중국 생태계 | Apache 2.0 | 산업용 IoT |
| Eclipse ThreadX | safety 인증 (DO-178B), 2024-01 오픈소스화 | EPL-2.0 | 구 Azure RTOS |
| Apache NuttX | POSIX-compliant, mission-critical | Apache 2.0 | PX4 드론, NASA Ingenuity |
| VxWorks | 항공·우주 hard real-time | 상용 ($) | 우주 탐사선, 군 |
| QNX | 마이크로커널, 자동차 | 상용 ($) | BMW, Bosch |
#이 시리즈에서
10 챕터에 걸쳐 RTOS의 기초를 다룹니다.
- Task·스케줄링·preemption (1-04)
- ISR·동기화 (5-6)
- Semaphore·Mutex·Queue (7-9)
- 실시간성 분석 (10)
Part 2부터는 내부 구현을 다룹니다. 스케줄러 자료구조, context switch, IPC 내부를 살펴봅니다.
#정리
- Super-Loop은 단순하지만 모든 작업이 직렬화됩니다. 빠른 작업이 느린 작업 뒤에 줄을 섭니다.
- RTOS는 Preemption + 우선순위 + 동기화 프리미티브의 조합입니다.
- Hard real-time은 RTOS 또는 bare-metal로만 달성할 수 있습니다.
- 작고 단순한 시스템엔 bare-metal이 더 깔끔합니다. 5+ task 또는 deadline < 10ms면 RTOS를 고려합니다.
다음 편에서는 Task와 Thread 개념을 다룹니다. TCB, 상태 머신, 생명 주기를 살펴봅니다.
#관련 항목
Practical RTOS Internals · 2 of 53
- 1Practical RTOS Internals — 실시간 커널 내부 분석 시리즈 소개
- 2RTOS가 필요한 이유 — 일반 OS와의 결정적 차이
- 3Task와 Thread 개념 — TCB·상태 머신·생명 주기 분석
- 4실시간 스케줄링 알고리즘 비교 — RR·Priority·EDF·RMS
- 5Preemption과 Cooperation — 강제 전환 vs 자발 양보
- 6인터럽트와 RTOS — ISR Context·Deferred Processing·FromISR API
- 7동기화 기초 분석 — Critical Section·Mutual Exclusion·Race Condition
- 8Semaphore 개념 분해 — Counting·Binary·P/V 연산
- 9Mutex 개념 분해 — Ownership·Recursive·Priority Inheritance
- 10큐와 메시지 패싱 — Producer-Consumer·Ring Buffer·전달 의미
- 11실시간성 분석 — Latency·Jitter·Deadline·WCET·RMA
- 12Ready List 자료구조 분석 — Linked List·Bitmap·O(1) Scheduler
- 13Blocked List 자료구조 — Timeout 정렬·Delta List·Two-List Scheme
- 14Scheduler 알고리즘 구현 추적 — Next-Task Selection 로직
- 15Context Switch 원리 분석 — 레지스터 저장·복원·Stack Frame
- 16ARM Cortex-M Context Switch — PendSV·MSP/PSP 어셈블리 추적
- 17ARM Cortex-A Context Switch — Mode 전환·SVC·Banked Registers
- 18RISC-V Context Switch 분석 — ECALL·mret·CSR
- 19RTOS Tick과 타이머 — SysTick·Generic Timer·configTICK_RATE_HZ
- 20Tickless 모드 구현 — Idle Tick Suppression·Sleep·Wake 보정
- 21Scheduler Latency 측정 기법 — GPIO Toggle·DWT·ftrace·cyclictest
- 22RTOS Tracing과 Observability — Tracealyzer·SystemView·ITM/ETM
- 23Critical Section 구현 비교 — IRQ Disable·BASEPRI·Spinlock
- 24Semaphore 내부 구현 추적 — Counter·Wait List·ISR-Safe Variant
- 25Mutex 내부 구현 추적 — Owner·Recursion Count·ISR 금지
- 26Priority Inversion 문제 — Mars Pathfinder 사례·Bounded vs Unbounded
- 27Priority Inheritance 구현 — Inherit·Disinherit·Chain
- 28Priority Ceiling Protocol — Immediate vs Original 비교
- 29Queue 내부 구현 추적 — Ring Buffer·2 Wait Lists·Atomic Send/Receive
- 30Event Group 분석 — Bit Flag·AND/OR Wait·Sync Barrier
- 31ISR-Safe API 설계 — FromISR 패턴·Higher Priority Wake·Deferred Work
- 32Deadlock 분석 — 4 조건·Wait-for Graph·Lock Ordering·Timeout
- 33Stream Buffer와 Message Buffer — FreeRTOS 10의 Lock-Free SPSC
- 34실시간 메모리 요구사항 — Determinism·Fragmentation·WCET
- 35FreeRTOS Heap_1~5 분석 — 5종 Allocator의 구조와 트레이드오프
- 36TLSF Allocator 분석 — Two-Level Segregated Fit O(1)
- 37Static Allocation — 컴파일 타임으로 동적 위험 제거하기
- 38Memory Pool — Fixed-Size Block Allocator의 단순함과 강력함
- 39Stack Overflow 탐지 — Canary·MPU·Watermark 3중 방어
- 40SMP RTOS 설계 — Ready List·Affinity·IPI·Load Balancing
- 41SMP Spinlock 구현 — LDREX/STREX·Ticket Lock·MCS·WFE/SEV
- 42Software Timer 분석 — Daemon Task·자료구조·ISR-Safe API
- 43RTOS System Call — SVC·ECALL·User/Kernel 분리·FreeRTOS-MPU
- 44TrustZone과 TF-M — Secure/Non-Secure·NSC Veneer·PSA
- 45AMP와 OpenAMP — Heterogeneous SoC·RPMsg·remoteproc
- 46C++ in RTOS — RAII·std::thread·ETL·Coroutine
- 47FreeRTOS 소스 분석 — tasks.c·queue.c·port.c 추적
- 48Zephyr 커널 분석 — k_thread·k_sem·Driver Model
- 49RT-Thread 분석 — Object 모델·Components·Smart·Studio
- 50RTOS 포팅 가이드 — 새 아키텍처에 옮기는 절차
- 51RTOS 선택 가이드 — Footprint·License·Certification·Ecosystem
- 52Apache NuttX 분석 — POSIX·PX4·NASA Ingenuity
- 53PREEMPT_RT Linux — Mainline 6.12·Xenomai 4·EVL
관련 글
Preemption과 Cooperation — 강제 전환 vs 자발 양보
Preemptive는 tick과 IRQ에서 강제로 전환합니다. Cooperative는 yield를 명시해야 합니다. latency와 predictability의 trade-off를 다룹니다.
C++ in RTOS — RAII·std::thread·ETL·Coroutine
RTOS C API를 C++ 객체로 감싸는 패턴을 정리합니다. RAII MutexGuard와 ScopedIRQDisable, std::thread/std::mutex의 한계와 직접 xTaskCreate가 결정성을 갖는 이유, ETL로 STL을 대체하는 법, C++20 coroutine을 RTOS 위에 얹는 방식까지 다룹니다.
실시간성 분석 — Latency·Jitter·Deadline·WCET·RMA
4 핵심 지표인 Latency, Jitter, Deadline, WCET를 다룹니다. Hard와 Soft real-time의 차이, Rate Monotonic Analysis로 schedulability를 증명하는 방법을 살펴봅니다.