본문으로 건너뛰기
Practical RTOS Internals · 33/53

실시간 메모리 요구사항 — Determinism·Fragmentation·WCET

· Hawk · 4분 읽기

#한 줄 요약

“RT 시스템에서는 allocation 시간이 bounded해야 합니다.” malloc의 가변 시간은 deadline을 위협합니다.

#일반 malloc의 문제

void *ptr = malloc(1024);

내부에서는 다음과 같은 단계가 일어납니다.

  • Size class별 free list를 순회합니다.
  • Block을 split합니다.
  • Coalesce 가능 여부를 검사합니다.
  • Heap lock을 acquire합니다.

그 결과 실행 시간이 수 µs에서 수 ms까지 들쭉날쭉합니다. WCET 보장이 어려워집니다.

#Fragmentation

#External Fragmentation

Free 영역: ████░░██░░███░░██ (총 free 6KB)
요청: [████████] (8 KB 연속)
→ 6 KB free 있어도 *연속 8 KB 없음* → 할당 실패

총 free 메모리가 충분해도 연속 영역이 부족해 할당이 실패하는 상황입니다.

#Internal Fragmentation

요청: 100 byte
할당: 128 byte block (size class) → 28 byte 낭비

Size class에 맞춰 올림 할당하면서 발생하는 낭비입니다.

#RT 시스템의 메모리 전략

전략장점단점
No mallocWCET 보장, 가장 안전유연성 ↓
Static allocation초기화 시 결정메모리 oversized
Memory poolO(1) alloc/free크기별 고정
TLSFO(1) bounded약간 복잡
Heap_4 (FreeRTOS)first-fitnon-bounded WCET
Heap_5 (FreeRTOS)non-contiguousfirst-fit

#Safety-Critical Standards

표준영향
MISRA CRule 21.3 — malloc/free 사용 금지 (Dir 4.12)
DO-178CLevel A·B에 dynamic allocation 회피
CERT CMEM34-C — realloc 후 NULL 검사
AUTOSARStatic allocation 우선
ISO 26262ASIL-D에 dynamic 자제

#Bounded Allocator — TLSF

Two-Level Segregated Fit (Masmano 2004):

First level — size 2^n bucket (16, 32, 64, ..., 64K)
Second level — 각 bucket을 2^m sub-bucket
e.g. 2 KB bucket → 2k, 2.25k, 2.5k, 2.75k
allocate(size):
- first level idx = floor(log2(size))
- second level idx = (size - 2^fl) / (2^(fl-4))
- bitmap에서 *가장 가까운 큰 bucket* 찾음 (CLZ)
- O(1)

자세한 내용은 4-03 편에서 다룹니다.

#Static Allocation 패턴

static uint8_t task1_stack[2048];
static StaticTask_t task1_tcb;
TaskHandle_t xTaskCreateStatic(
task1_function, "task1", 2048 / sizeof(StackType_t),
NULL, 5, task1_stack, &task1_tcb);

FreeRTOS, Zephyr, Wind River 모두 static variant API를 제공합니다.

#Memory Pool

#define POOL_SIZE 32
#define BLOCK_SIZE 64
static uint8_t pool_buf[POOL_SIZE * BLOCK_SIZE];
static struct { uint8_t *free_list; mutex_t lock; } pool;
void *pool_alloc(void) {
/* O(1) — free list pop */
}
void pool_free(void *ptr) {
/* O(1) — free list push */
}

Block 크기를 고정해 fragmentation이 0이 되고 WCET도 보장됩니다.

#Buddy Allocator

Linux 커널과 일부 RTOS에서 사용하는 방식입니다.

초기: [ 2MB ]
요청 256KB:
[256K][256K][512K][1M]
할당하나 free 3개

Power-of-2 단위로 split하고 coalesce하므로 bounded O(log N)을 보장합니다.

#측정: Heap 상태

/* FreeRTOS */
size_t free = xPortGetFreeHeapSize();
size_t min = xPortGetMinimumEverFreeHeapSize();
HeapStats_t stats;
vPortGetHeapStats(&stats);
/* stats.xMaximumFreeBlockSize, xNumberOfFreeBlocks */

xMaximumFreeBlockSize가 작은데 xNumberOfFreeBlocks가 많으면 fragmentation이 심하다는 신호입니다.

#Embedded에서 DRAM과 SRAM 분리

__attribute__((section(".dtcm"))) uint8_t fast_buf[8192]; /* TCM */
__attribute__((section(".sdram"))) uint8_t big_buf[256 * 1024]; /* ext SDRAM */
__attribute__((section(".sram"))) uint8_t medium_buf[16384];

Memory region마다 속도와 용량의 trade-off가 다릅니다. Linker script로 영역을 명시합니다.

#자동차와 항공: Static Only

ASIL-D ECU에서는

  • 모든 task stack을 static으로 둡니다.
  • 모든 buffer를 compile-time fixed로 잡습니다.
  • 모든 message queue를 static으로 만듭니다.
  • malloc 자체를 제외합니다.

KSLV-II 누리호 flight computer에는 malloc이 아예 없습니다. 모든 메모리는 부팅 시점에 고정됩니다.

#DO-178C Level A에서 Heap 사용

가능은 하지만 극도로 어렵습니다.

  • WCET 분석
  • Worst-case fragmentation 증명
  • 모든 path coverage
  • Robustness testing

이 모든 항목을 증명하는 비용이 크기 때문에 보통은 heap을 피합니다. ITAR이나 NIST 같은 표준도 같은 방향을 권장합니다.

#자주 하는 실수

⚠️ Production 코드에서 heap을 안일하게 사용하는 경우

/* Production code에 */
void *buf = malloc(rand() % 1024);

Embedded 환경에서는 malloc 자체를 피하는 편이 안전합니다.

⚠️ Fragmentation을 인지하지 못하는 경우

malloc(100); free();
malloc(200); free();
malloc(50); free();
/* → free list 흩어짐 */
malloc(150); /* ← 가능하나 fragmented free 영역 search */

이 패턴이 반복되면 free list가 흩어집니다. memory pool이나 TLSF로 대체해야 합니다.

⚠️ Stack을 dynamic하게 할당하는 경우

xTaskCreate(... uxStackDepth ... );
/* → heap에서 stack 할당 */

대신 xTaskCreateStatic을 사용합니다.

⚠️ ISR에서 malloc을 호출하는 경우

ISR: malloc(...); /* ✗ heap lock — deadlock */

ISR에서는 static buffer만 사용해야 합니다.

#정리

  • RT 메모리는 bounded allocation과 no fragmentation을 동시에 충족해야 합니다.
  • Safety-critical 표준(MISRA, DO-178C, ASIL)에서는 malloc을 금지하거나 회피합니다.
  • 대안으로 static 할당, memory pool, TLSF, buddy allocator가 있습니다.
  • 상태 측정은 FreeRTOS의 vPortGetHeapStats로 확인합니다.
  • 발사체나 자동차 ECU는 거의 static-only로 운영합니다.

다음 편에서는 FreeRTOS Heap_1부터 Heap_5까지 다섯 가지 구현을 비교합니다.

#관련 항목

Practical RTOS Internals · 34 of 53

  1. 1Practical RTOS Internals — 실시간 커널 내부 분석 시리즈 소개
  2. 2RTOS가 필요한 이유 — 일반 OS와의 결정적 차이
  3. 3Task와 Thread 개념 — TCB·상태 머신·생명 주기 분석
  4. 4실시간 스케줄링 알고리즘 비교 — RR·Priority·EDF·RMS
  5. 5Preemption과 Cooperation — 강제 전환 vs 자발 양보
  6. 6인터럽트와 RTOS — ISR Context·Deferred Processing·FromISR API
  7. 7동기화 기초 분석 — Critical Section·Mutual Exclusion·Race Condition
  8. 8Semaphore 개념 분해 — Counting·Binary·P/V 연산
  9. 9Mutex 개념 분해 — Ownership·Recursive·Priority Inheritance
  10. 10큐와 메시지 패싱 — Producer-Consumer·Ring Buffer·전달 의미
  11. 11실시간성 분석 — Latency·Jitter·Deadline·WCET·RMA
  12. 12Ready List 자료구조 분석 — Linked List·Bitmap·O(1) Scheduler
  13. 13Blocked List 자료구조 — Timeout 정렬·Delta List·Two-List Scheme
  14. 14Scheduler 알고리즘 구현 추적 — Next-Task Selection 로직
  15. 15Context Switch 원리 분석 — 레지스터 저장·복원·Stack Frame
  16. 16ARM Cortex-M Context Switch — PendSV·MSP/PSP 어셈블리 추적
  17. 17ARM Cortex-A Context Switch — Mode 전환·SVC·Banked Registers
  18. 18RISC-V Context Switch 분석 — ECALL·mret·CSR
  19. 19RTOS Tick과 타이머 — SysTick·Generic Timer·configTICK_RATE_HZ
  20. 20Tickless 모드 구현 — Idle Tick Suppression·Sleep·Wake 보정
  21. 21Scheduler Latency 측정 기법 — GPIO Toggle·DWT·ftrace·cyclictest
  22. 22RTOS Tracing과 Observability — Tracealyzer·SystemView·ITM/ETM
  23. 23Critical Section 구현 비교 — IRQ Disable·BASEPRI·Spinlock
  24. 24Semaphore 내부 구현 추적 — Counter·Wait List·ISR-Safe Variant
  25. 25Mutex 내부 구현 추적 — Owner·Recursion Count·ISR 금지
  26. 26Priority Inversion 문제 — Mars Pathfinder 사례·Bounded vs Unbounded
  27. 27Priority Inheritance 구현 — Inherit·Disinherit·Chain
  28. 28Priority Ceiling Protocol — Immediate vs Original 비교
  29. 29Queue 내부 구현 추적 — Ring Buffer·2 Wait Lists·Atomic Send/Receive
  30. 30Event Group 분석 — Bit Flag·AND/OR Wait·Sync Barrier
  31. 31ISR-Safe API 설계 — FromISR 패턴·Higher Priority Wake·Deferred Work
  32. 32Deadlock 분석 — 4 조건·Wait-for Graph·Lock Ordering·Timeout
  33. 33Stream Buffer와 Message Buffer — FreeRTOS 10의 Lock-Free SPSC
  34. 34실시간 메모리 요구사항 — Determinism·Fragmentation·WCET
  35. 35FreeRTOS Heap_1~5 분석 — 5종 Allocator의 구조와 트레이드오프
  36. 36TLSF Allocator 분석 — Two-Level Segregated Fit O(1)
  37. 37Static Allocation — 컴파일 타임으로 동적 위험 제거하기
  38. 38Memory Pool — Fixed-Size Block Allocator의 단순함과 강력함
  39. 39Stack Overflow 탐지 — Canary·MPU·Watermark 3중 방어
  40. 40SMP RTOS 설계 — Ready List·Affinity·IPI·Load Balancing
  41. 41SMP Spinlock 구현 — LDREX/STREX·Ticket Lock·MCS·WFE/SEV
  42. 42Software Timer 분석 — Daemon Task·자료구조·ISR-Safe API
  43. 43RTOS System Call — SVC·ECALL·User/Kernel 분리·FreeRTOS-MPU
  44. 44TrustZone과 TF-M — Secure/Non-Secure·NSC Veneer·PSA
  45. 45AMP와 OpenAMP — Heterogeneous SoC·RPMsg·remoteproc
  46. 46C++ in RTOS — RAII·std::thread·ETL·Coroutine
  47. 47FreeRTOS 소스 분석 — tasks.c·queue.c·port.c 추적
  48. 48Zephyr 커널 분석 — k_thread·k_sem·Driver Model
  49. 49RT-Thread 분석 — Object 모델·Components·Smart·Studio
  50. 50RTOS 포팅 가이드 — 새 아키텍처에 옮기는 절차
  51. 51RTOS 선택 가이드 — Footprint·License·Certification·Ecosystem
  52. 52Apache NuttX 분석 — POSIX·PX4·NASA Ingenuity
  53. 53PREEMPT_RT Linux — Mainline 6.12·Xenomai 4·EVL