Priority Ceiling Protocol — Immediate vs Original 비교
#한 줄 요약
“Mutex에 ceiling을 정적으로 부여하고 take 즉시 boost한다” — PI의 take-then-conflict 방식과 달리, 사전에 적극적으로 막는 접근입니다.
이번 글에서는 Priority Ceiling Protocol(PCP)을 살펴봅니다. PI보다 덜 알려졌지만 deadlock 보장 측면에서 강력합니다.
#PCP 동작
각 mutex M에 priority ceiling C(M)을 부여합니다. C(M)은 이 mutex를 lock할 task 중 최고 priority입니다.
예시 — mutex X: C(X) = 5 (T_high의 priority)
T_low(priority 1)가 X를 take하는 즉시 →T_low의 priority가 5로 boost.T_med(priority 3)가 ready 되어도 시작 못 함 (T_low의 5 > 3).T_low가 X를 release하면 priority가 1로 복원.
PCP는 take 즉시 boost합니다. PI는 conflict가 발생한 후에 boost합니다.
#Immediate PCP vs Original PCP
| Immediate | Original | |
|---|---|---|
| Boost 시점 | Take 즉시 | Take + conflict 시 |
| 구현 | 단순 | 복잡 (system ceiling 계산) |
| Preemption | 자기 ceiling 위 task만 | 동일 + 다른 mutex ceiling |
| 사용 | VxWorks·일부 RTOS | 학술 |
#Deadlock 방지 증명
T1이M1보유 (ceiling 5) →T1priority = 5.T1이M2(ceiling 5)를 시도.- 만약
T2가M2를 보유 중이면 →T2priority ≥ 5 (M2의 ceiling). T2의 priority가 5 이상이면T1은 시작도 못 함 — 모순.- 따라서
T2가M2를 보유할 수 없고,M2는 free 상태이므로T1이 acquire 가능.
결론은 PCP를 쓰면 deadlock이 불가능하다는 것입니다.
이것이 PCP의 가장 큰 매력입니다. Lock order에 무관하게 deadlock이 막힙니다.
#구현 — RTOS API
// VxWorkssemMCreate(SEM_PRIORITY_CEILING, ceiling_priority);
// Zephyr (config 옵션)CONFIG_PRIORITY_CEILING=ystruct k_mutex mtx;k_mutex_init(&mtx);mtx.ceiling = HIGH_PRIO;FreeRTOS는 PCP를 지원하지 않습니다. PI만 제공합니다.
#System Ceiling — Original PCP
SystemCeiling(t) = max(C(M) for M in locked mutexes at time t)
T_new가 lock을 시도할 때
T_new->priority > SystemCeiling이면 proceed.- 그 외에는 block.
구현이 복잡합니다. 그래서 Immediate 방식이 훨씬 흔합니다.
#PI vs PCP 비교
| PIP | Immediate PCP | |
|---|---|---|
| Boost 발생 | Conflict 시 | Take 즉시 |
| Priority 사전 결정 | 불필요 | 필수 |
| Deadlock 방지 | ✗ | ✓ |
| Blocking 길이 | bounded (by critical) | bounded (이론 동일) |
| Unnecessary boost | 자주 발생 | 자주 발생 |
| 구현 복잡도 | 중 | 저 |
| 호환성 | 동적 시스템 | 정적 분석 가능 |
#단점 — Unnecessary Boost
T_high가 mutex를 사용하지 않는 시점에도 T_low가 T_high level로 boost되어, T_med·T_low가 항상 starved되고 시스템 throughput이 감소합니다.
PCP는 worst case 안전하지만 best case 낭비가 심합니다.
#적용 사례
| RTOS | Default | 옵션 |
|---|---|---|
| FreeRTOS | PI | PCP 없음 |
| Zephyr | PI | CONFIG_PRIORITY_CEILING_PROTOCOL |
| VxWorks | PI or PCP | 양쪽 |
| QNX | PI | PCP |
| RTAI | PI | — |
대부분의 시스템은 PI를 기본으로 씁니다. PCP는 deadlock 회피가 critical인 환경에서 선택됩니다.
#Immediate PCP 구현 예
typedef struct { int locked; int ceiling; TaskHandle_t owner; int owner_orig_prio;} mutex_pcp_t;
int mutex_pcp_take(mutex_pcp_t *m) { taskENTER_CRITICAL(); if (m->locked) { /* 누가 보유 중 — 대기 (priority ≥ ceiling이므로 wait가 없어야 정상) */ taskEXIT_CRITICAL(); return ERROR; } m->locked = 1; m->owner = current_task(); m->owner_orig_prio = current_task()->prio; /* Boost */ current_task()->prio = m->ceiling; taskEXIT_CRITICAL(); return 0;}
int mutex_pcp_give(mutex_pcp_t *m) { taskENTER_CRITICAL(); if (m->owner != current_task()) { taskEXIT_CRITICAL(); return ERROR; } /* Restore */ current_task()->prio = m->owner_orig_prio; m->locked = 0; m->owner = NULL; taskEXIT_CRITICAL(); return 0;}#자주 하는 실수
⚠️ Ceiling 계산 실수
max(이 mutex를 쓰는 task priority)로 정확히 잡아야 합니다. 빠지면 PI가 적용되지 않아 inversion이 발생합니다.
⚠️ FreeRTOS에서 PCP를 시도
지원하지 않습니다. PI만 가능합니다.
⚠️ 동적 시스템에 PCP 적용
새 task가 더 높은 priority로 mutex를 사용하면 ceiling을 재계산해야 합니다. 동적 환경에서는 어렵습니다.
#정리
- PCP는 mutex에 정적 ceiling을 두고 take 즉시 boost합니다.
- Immediate vs Original 중 Immediate가 훨씬 흔합니다.
- Deadlock 방지가 자동이며 lock order에 무관합니다.
- Priority 사전 결정이 필요해서 동적 시스템에서는 적용이 어렵습니다.
- 채택 사례는 VxWorks·Zephyr입니다. FreeRTOS는 PI만 지원합니다.
다음 편은 Queue 내부 구현입니다.
#관련 항목
Practical RTOS Internals · 28 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
관련 글
PREEMPT_RT Linux — Mainline 6.12·Xenomai 4·EVL
2024년 9월 Linux 6.12 mainline에 합류한 PREEMPT_RT의 핵심 변경을 정리하고, Xenomai 4·EVL과 함께 RTOS와의 선택 기준을 비교합니다. threaded IRQ·sleeping spinlock·cyclictest까지 한 지도에 모읍니다.
Apache NuttX 분석 — POSIX·PX4·NASA Ingenuity
NuttX의 POSIX-compliant 구조를 따라가며 PX4 autopilot과 NASA Ingenuity 화성 헬리콥터 채택 배경을 정리합니다. Flat/Protected/Kernel 빌드, VFS, 네트워크, NSH, micro-ROS 통합까지 한 지도로 모읍니다.
RTOS 선택 가이드 — Footprint·License·Certification·Ecosystem
FreeRTOS·Zephyr·ThreadX·RT-Thread·NuttX·VxWorks·QNX·INTEGRITY·SafeRTOS·µC/OS·PX5를 한 표에 모아 비교합니다. IoT·자동차·항공·산업·의료·웨어러블·드론별 추천과 결정 기준을 정리합니다.