Task와 Thread 개념 — TCB·상태 머신·생명 주기 분석
#한 줄 요약
“Task = stack + TCB + state”입니다. 세 가지가 갖춰지면 RTOS가 task로 인식합니다.
#Task의 본질 — 세 가지 구성
| 요소 | 역할 |
|---|---|
| Stack | 함수 호출·로컬 변수·인터럽트 시 레지스터 저장 |
| TCB (Task Control Block) | 메타데이터 (state, priority, stack pointer, …) |
| State | Running / Ready / Blocked / … |
이 셋이 있으면 RTOS scheduler가 교대로 실행할 수 있습니다.
#TCB — Task Control Block
FreeRTOS의 TCB_t 발췌:
typedef struct tskTaskControlBlock { volatile StackType_t *pxTopOfStack; // 현재 스택 포인터 ListItem_t xStateListItem; // ready/blocked list에 link ListItem_t xEventListItem; // event wait list에 link UBaseType_t uxPriority; // 현재 우선순위 StackType_t *pxStack; // 스택 시작 주소 char pcTaskName[configMAX_TASK_NAME_LEN];
UBaseType_t uxBasePriority; // 원래 우선순위 (PI 후 복원용) UBaseType_t uxMutexesHeld; // 보유 mutex 수 /* ... 기타 ... */} tskTCB;핵심은 pxTopOfStack이 context switch의 모든 것이라는 점입니다. 다른 task로 전환할 때 현재 task의 모든 레지스터를 그 stack에 push하고, 다음 task의 pxTopOfStack에서 pop합니다.
#Stack — Task별 독립
각 task가 자기 stack을 보유합니다. 보통 256-2048 byte입니다. 함수 호출 깊이, 로컬 변수, ISR 시 자동 push되는 레지스터를 모두 담습니다.
void pid_task(void *arg) { int local_var = 0; // stack에 update(); // 함수 호출 → return address stack에 ...}
// 생성 시 stack size 명시xTaskCreate(pid_task, "PID", 256, NULL, 3, NULL);// ^^^// 256 × sizeof(StackType_t) = 1 KB (32-bit)⚠️ Stack overflow가 임베디드 디버깅의 80%를 차지합니다. Canary, MPU, configCHECK_FOR_STACK_OVERFLOW를 활용합니다.
#Task State — 5 상태 머신
| State | 의미 |
|---|---|
| Running | 지금 CPU 차지. 한 코어당 정확히 1개입니다. |
| Ready | 실행 가능, CPU 대기 (scheduler가 골라줘야 합니다) |
| Blocked | 이벤트 대기 (semaphore·queue·delay·notification) |
| Suspended | 명시적 vTaskSuspend. scheduler가 고려하지 않습니다. |
| Deleted | vTaskDelete. TCB만 살아있고 idle task가 정리합니다. |
#전이 — 누가 트리거하나
| 전이 | 트리거 |
|---|---|
| Ready → Running | Scheduler (preemption 또는 yield 시) |
| Running → Ready | Higher-priority task ready 됨 (preempted) |
| Running → Blocked | task 자신이 wait API 호출 |
| Running → Suspended | 다른 task가 vTaskSuspend() |
| Blocked → Ready | 이벤트 발생 (semaphore give, queue send, timeout) |
| Suspended → Ready | vTaskResume() |
| 모두 → Deleted | vTaskDelete() — 자기 또는 다른 task가 호출 |
💡 Running → Blocked는 자발적이고, Running → Ready는 강제적입니다. 둘은 본질적으로 다릅니다.
#Task 생성 — FreeRTOS 예
TaskHandle_t pid_handle = NULL;
BaseType_t result = xTaskCreate( pid_task, // 함수 "PID", // 이름 (디버깅용) 256, // stack size (words) NULL, // 함수에 전달할 인자 3, // priority (0 = lowest) &pid_handle // 핸들 (제어용));
if (result != pdPASS) { /* 메모리 부족 — heap 확인 */}이 한 줄에서 RTOS는 다음 일을 합니다.
- TCB를 할당합니다 (heap_X 또는 static).
- Stack을 할당합니다.
- Stack에 가짜 context (return address = pid_task 등)를 쌓습니다.
- Ready list에 추가합니다.
- 만약 새 task가 더 높은 priority면 즉시 preempt합니다.
#Task 함수의 패턴
void pid_task(void *arg) { /* 초기화 (1회) */ init_pid();
/* 메인 루프 */ while (1) { wait_for_event(); // Blocked로 들어감 process(); // Ready → Running }
/* 도달 안 함. 도달하면 *반드시 vTaskDelete()*. */ vTaskDelete(NULL);}⚠️ task 함수에서 return 하면 RTOS가 충돌합니다. 명시적
vTaskDelete(NULL)또는 무한루프를 사용합니다.
#Idle Task — 항상 존재하는 최저 우선순위 task
void prvIdleTask(void *pvParameters) { while (1) { /* 1. Deleted task 정리 */ prvCheckTasksWaitingTermination(); /* 2. Tickless idle 진입 (저전력) */ if (configUSE_TICKLESS_IDLE) portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime); }}다른 모든 task가 Blocked나 Suspended일 때만 실행됩니다. priority는 0입니다. CPU sleep 모드 진입을 trigger합니다.
#Static vs Dynamic 할당
| Static | Dynamic (heap) | |
|---|---|---|
| API | xTaskCreateStatic | xTaskCreate |
| 메모리 | 컴파일 타임 결정 | 런타임 heap_X에서 |
| Fragmentation | 없음 | 가능 |
| Code size | 약간 작음 | malloc 코드 포함 |
| 안전 인증 | 선호 (MISRA·DO-178C) | 회피 |
Safety-critical (자동차·항공·의료) 분야는 static을 선호하고, 일반 IoT는 dynamic이 흔합니다.
#Thread vs Task — 용어 차이
RTOS 세계의 전통적 용어는 task입니다. POSIX, Zephyr, 일부 자료에서는 thread를 씁니다.
| 용어 | 사용처 |
|---|---|
| Task | FreeRTOS, ThreadX, MicroC/OS, VxWorks |
| Thread | Zephyr, POSIX (pthread), Windows |
| Process | Linux (별도 메모리 공간) |
본질은 같습니다. 독립 stack과 공유 메모리를 가집니다. Linux의 Process만 격리된 메모리를 가집니다.
#자주 하는 실수
⚠️ Stack 너무 작음
256 word (1 KB)는 대부분 충분하지만, 큰 로컬 배열, printf, 재귀를 사용하면 오버플로가 발생합니다. uxTaskGetStackHighWaterMark()로 사용량을 측정합니다.
⚠️ Task에서 return
위에서 언급했듯이 무한 루프 + delay가 표준 패턴입니다.
⚠️ ISR에서 normal API 호출
ISR에서 xQueueSend()를 호출하면 데드락이나 크래시가 발생합니다. FromISR 변종을 사용합니다.
⚠️ Priority 인플레이션
모든 task를 high priority로 설정해도 의미가 없습니다. task 간 상대적 순서가 중요합니다.
#정리
- Task는 Stack과 TCB, State로 구성됩니다.
- 5 상태가 있습니다 (Running, Ready, Blocked, Suspended, Deleted).
- 전이는 자발적(blocked) 또는 강제적(preempted)입니다.
- Idle Task가 항상 존재하여 tickless와 정리를 담당합니다.
- Stack overflow는 임베디드 디버깅 1위 원인입니다. canary와 MPU가 필수입니다.
다음 편에서는 스케줄링 알고리즘을 다룹니다. Round Robin, Priority-based, EDF, Rate Monotonic을 살펴봅니다.
#관련 항목
Practical RTOS Internals · 3 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
관련 글
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 위에 얹는 방식까지 다룹니다.
인터럽트와 RTOS — ISR Context·Deferred Processing·FromISR API
ISR은 task가 아니므로 context도 따로 관리됩니다. Long work는 deferred task로 넘기고, FromISR API 패턴을 씁니다.
Preemption과 Cooperation — 강제 전환 vs 자발 양보
Preemptive는 tick과 IRQ에서 강제로 전환합니다. Cooperative는 yield를 명시해야 합니다. latency와 predictability의 trade-off를 다룹니다.
이 글을 참조하는 글 (5)
- Ready List 자료구조 분석 — Linked List·Bitmap·O(1) Scheduler— Practical RTOS Internals
- 실시간 스케줄링 알고리즘 비교 — RR·Priority·EDF·RMS— Practical RTOS Internals
- RTOS가 필요한 이유 — 일반 OS와의 결정적 차이— Practical RTOS Internals
- RTOS Task 설계 패턴 — 우선순위·스택·State Machine— Modern Embedded Recipes
- RTOS 도입 결정 분석 — Super Loop vs RTOS 트레이드오프— Modern Embedded Recipes