실시간 스케줄링 알고리즘 비교 — RR·Priority·EDF·RMS
#한 줄 요약
“임베디드 RTOS는 Fixed-Priority Preemptive + RR (same priority)입니다.” 95% 케이스의 답이 됩니다.
#4가지 주요 알고리즘
| 알고리즘 | 결정 기준 | 사용처 |
|---|---|---|
| Round Robin (RR) | 시간 할당 (time slice) 회전 | 같은 우선순위 task 간 |
| Fixed-Priority Preemptive | 고정 우선순위 | 99% RTOS 기본 |
| Earliest Deadline First (EDF) | 다음 deadline 임박 task 우선 | 학술·일부 hard real-time |
| Rate Monotonic (RM) | 짧은 주기 task 우선 | 분석 가능한 hard real-time |
대부분의 임베디드 RTOS는 “Fixed-Priority Preemptive + 같은 priority 내 RR” 혼합 방식을 씁니다.
#Round Robin (RR)
같은 우선순위 task들이 time slice 단위로 차례로 실행됩니다.
시간 →[T1: 10ms][T2: 10ms][T3: 10ms][T1: 10ms]...#특징
- 공정성: 모든 task가 같은 CPU 시간을 받습니다.
- 응답성: 새 task가 와도 최대 1 slice만 대기합니다.
- deadline 보장 X: 결정성이 없습니다.
#FreeRTOS 적용
configUSE_TIME_SLICING = 1이 기본값입니다. 같은 priority 내에서만 RR이 동작하고, 다른 priority는 무시합니다.
xTaskCreate(taskA, "A", 256, NULL, 3, NULL); // Priority 3xTaskCreate(taskB, "B", 256, NULL, 3, NULL); // Priority 3 → A와 RRxTaskCreate(taskC, "C", 256, NULL, 5, NULL); // Priority 5 → A·B preempt#Fixed-Priority Preemptive
가장 흔한 RTOS 모델입니다. 각 task에 고정 우선순위를 부여하고, 항상 가장 높은 ready task가 실행됩니다.
#동작
// Priority 3 PID taskvoid pid_task(void *arg) { while (1) { compute_pid(); vTaskDelay(1); // Blocked → 1ms 후 Ready }}
// Priority 1 logger taskvoid log_task(void *arg) { while (1) { log_data(); // 5 ms 걸림 // ↑ 도중 PID task가 ready 되면 *즉시 preempt* }}PID가 매 ms ready 되면 log는 5ms 작업을 여러 번 쪼개 진행합니다. 결국 PID가 100%까지 차지할 수 있습니다.
#우선순위 선택 — Rate Monotonic Theorem
Liu & Layland (1973)에 따르면 짧은 주기 task에 더 높은 priority를 주는 것이 최적입니다.
| Task | 주기 | Priority |
|---|---|---|
| PID | 1 ms | 5 |
| 센서 | 10 ms | 4 |
| 로그 | 100 ms | 3 |
| UI | 1000 ms | 2 |
이 규칙이 **Rate Monotonic Scheduling (RMS)**입니다. 분석 가능한 schedulability bound가 존재합니다.
#Rate Monotonic Schedulability
n개 task의 utilization 합이 다음 한계 안이면 항상 deadline 만족이 보장됩니다.
U = Σ (Ci / Ti) ≤ n × (2^(1/n) − 1)n이 무한대일 때 한계는 ln(2) ≈ 0.693입니다. 즉 CPU 69%까지 사용해도 안전합니다.
#예 — 3 task
| Task | Ci (실행시간) | Ti (주기) | Ci/Ti |
|---|---|---|---|
| A | 1 ms | 4 ms | 0.25 |
| B | 2 ms | 5 ms | 0.40 |
| C | 1 ms | 10 ms | 0.10 |
| 합 | 0.75 |
n=3일 때 한계는 3 × (2^(1/3) − 1) ≈ 0.78입니다. 0.75 < 0.78이므로 schedulable합니다.
만약 한계를 초과해도 deadline 만족이 가능합니다. Response Time Analysis (RTA)로 정확히 확인할 수 있습니다.
#Earliest Deadline First (EDF)
각 task의 다음 absolute deadline이 가장 가까운 것이 우선합니다.
시점 t=0: T1 deadline t=10 T2 deadline t=15 T3 deadline t=8→ T3 실행
시점 t=8: T1 deadline t=10 T2 deadline t=15→ T1 실행#장점
- CPU 100%까지 사용 가능합니다 (이론적 최적입니다).
- 동적 우선순위라서 preemption이 deadline 임박에 의해 결정됩니다.
#단점
- 구현이 복잡합니다. 매 작업마다 deadline을 비교하고 dynamic priority를 재계산해야 합니다.
- Overrun 시 cascading failure가 발생합니다. 한 task가 늦으면 다음 task들도 연쇄로 miss합니다.
- 임베디드 RTOS에서 거의 채택되지 않습니다. FreeRTOS, Zephyr, VxWorks 모두 RMS 기반입니다.
ERIKA Enterprise, MIRTOS 같은 학술 OS에 구현되어 있습니다. 자동차 안전 시스템에서 일부 채택합니다.
#Cooperative Scheduling
각 task가 자발적 yield를 호출할 때만 전환됩니다. Preemption이 없습니다.
void taskA(void *arg) { while (1) { do_stuff(); taskYIELD(); // 다른 task에 양보 }}#장점
- 단순합니다 (race condition이 적습니다).
- 결정적입니다 (어디서 전환할지 명확합니다).
#단점
- 한 task가 yield하지 않으면 전체가 멈춥니다.
- 실시간성이 없습니다.
configUSE_PREEMPTION = 0이면 FreeRTOS가 cooperative로 동작합니다. 작은 시스템이나 디버그용입니다.
#Priority Inversion (예고)
Mars Pathfinder (1997)는 화성 탐사선이 지속적으로 reset되는 문제를 겪었습니다. 원인은 다음과 같았습니다.
T_high(priority 5) — mutex 대기T_med(priority 3) — 계속 실행T_low(priority 1) — mutex 보유,T_med에 preempt 당함 → 영원히 못 해제
해결책은 Priority Inheritance입니다 (3-04, 3-05 챕터에서 자세히 다룹니다).
#SMP — Multi-Core 스케줄링
다중 코어에서는 한 번에 N개 task를 동시에 실행합니다. 두 가지 접근이 있습니다.
#1. Global Scheduling
[Ready Queue] ← 모든 코어 공유Core 0: T_high1Core 1: T_high2장점은 load balancing이 자동이라는 점입니다. 단점은 lock contention과 cache locality가 깨진다는 점입니다.
#2. Partitioned Scheduling
[Ready Queue Core 0] ← T1·T2·T3[Ready Queue Core 1] ← T4·T5장점은 cache friendly하다는 점입니다. 단점은 manual balancing이 필요하다는 점입니다.
FreeRTOS SMP (10.4+)는 partitioned + task affinity 방식을 씁니다. Zephyr SMP는 global with affinity hint를 씁니다.
#임베디드 RTOS 표준 답
대부분의 임베디드 시스템에서 Fixed-Priority Preemptive + RMS 우선순위 + RR (same priority) 조합을 사용합니다.
// 주기별 priority 할당 (RMS)xTaskCreate(pid_1ms, "PID", 256, NULL, 5, NULL); // 1 ms 주기xTaskCreate(sensor_10ms,"SENS", 256, NULL, 4, NULL); // 10 msxTaskCreate(log_100ms, "LOG", 256, NULL, 3, NULL); // 100 msxTaskCreate(ui_1000ms, "UI", 512, NULL, 2, NULL); // 1 s
// 같은 priority면 RR 자동xTaskCreate(net_handler1,"NET1", 256, NULL, 3, NULL); // log와 RRxTaskCreate(net_handler2,"NET2", 256, NULL, 3, NULL);이 패턴이 95% 임베디드 시스템에서 정답입니다.
#자주 하는 실수
⚠️ 모든 task에 같은 priority
RR로 떨어져 실시간성이 없습니다. 주기와 중요도에 따라 분배해야 합니다.
⚠️ Critical task에 낮은 priority
PID나 안전 제어가 낮은 priority면 다른 task에 막힙니다. 최고 priority를 주어야 합니다.
⚠️ Priority 너무 많은 단계
32단계를 다 쓰면 관리가 복잡해집니다. 보통 5-10단계로 충분합니다.
⚠️ CPU 100% 가정
RMS는 69%까지가 안전합니다. 80%를 넘으면 deadline miss 위험이 있습니다.
#정리
- Round Robin은 같은 priority 내에서 시간 회전을 합니다.
- Fixed-Priority Preemptive가 임베디드 RTOS의 표준입니다.
- Rate Monotonic은 짧은 주기 task를 우선하고 분석이 가능합니다 (≤69% CPU).
- EDF는 이론적 최적이지만 임베디드에서는 채택되지 않습니다.
- 대부분의 시스템에서 주기 기반 priority + RMS가 답입니다.
다음 편에서는 Preemption vs Cooperation을 다룹니다. 강제 전환과 자발 양보의 trade-off를 살펴봅니다.
#관련 항목
Practical RTOS Internals · 4 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
관련 글
Practical RTOS Internals — 실시간 커널 내부 분석 시리즈 소개
RTOS를 사용하는 것이 아니라 이해하고 구현하는 법. Scheduler, context switch, memory allocator의 내부 동작을 소스 코드 수준에서 분석합니다.
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 위에 얹는 방식까지 다룹니다.
Scheduler Latency 측정 기법 — GPIO Toggle·DWT·ftrace·cyclictest
ISR 종료 → ready task 실행까지의 시간. 측정 방법과 worst-case 추적.
이 글을 참조하는 글 (5)
- 실시간성 분석 — Latency·Jitter·Deadline·WCET·RMA— Practical RTOS Internals
- Preemption과 Cooperation — 강제 전환 vs 자발 양보— Practical RTOS Internals
- Task와 Thread 개념 — TCB·상태 머신·생명 주기 분석— Practical RTOS Internals
- RTOS Scheduler 동작 분석 — Tick·Context Switch·Yield— Modern Embedded Recipes
- RTOS Task 설계 패턴 — 우선순위·스택·State Machine— Modern Embedded Recipes