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

Task와 Thread 개념 — TCB·상태 머신·생명 주기 분석

· Hawk · 5분 읽기

#한 줄 요약

“Task = stack + TCB + state”입니다. 세 가지가 갖춰지면 RTOS가 task로 인식합니다.

#Task의 본질 — 세 가지 구성

요소역할
Stack함수 호출·로컬 변수·인터럽트 시 레지스터 저장
TCB (Task Control Block)메타데이터 (state, priority, stack pointer, …)
StateRunning / 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;

핵심은 pxTopOfStackcontext 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 상태 머신

Task 5-state machine

State의미
Running지금 CPU 차지. 한 코어당 정확히 1개입니다.
Ready실행 가능, CPU 대기 (scheduler가 골라줘야 합니다)
Blocked이벤트 대기 (semaphore·queue·delay·notification)
Suspended명시적 vTaskSuspend. scheduler가 고려하지 않습니다.
DeletedvTaskDelete. TCB만 살아있고 idle task가 정리합니다.

#전이 — 누가 트리거하나

전이트리거
Ready → RunningScheduler (preemption 또는 yield 시)
Running → ReadyHigher-priority task ready 됨 (preempted)
Running → Blockedtask 자신이 wait API 호출
Running → Suspended다른 task가 vTaskSuspend()
Blocked → Ready이벤트 발생 (semaphore give, queue send, timeout)
Suspended → ReadyvTaskResume()
모두 → DeletedvTaskDelete() — 자기 또는 다른 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는 다음 일을 합니다.

  1. TCB를 할당합니다 (heap_X 또는 static).
  2. Stack을 할당합니다.
  3. Stack에 가짜 context (return address = pid_task 등)를 쌓습니다.
  4. Ready list에 추가합니다.
  5. 만약 새 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 할당

StaticDynamic (heap)
APIxTaskCreateStaticxTaskCreate
메모리컴파일 타임 결정런타임 heap_X에서
Fragmentation없음가능
Code size약간 작음malloc 코드 포함
안전 인증선호 (MISRA·DO-178C)회피

Safety-critical (자동차·항공·의료) 분야는 static을 선호하고, 일반 IoT는 dynamic이 흔합니다.

#Thread vs Task — 용어 차이

RTOS 세계의 전통적 용어task입니다. POSIX, Zephyr, 일부 자료에서는 thread를 씁니다.

용어사용처
TaskFreeRTOS, ThreadX, MicroC/OS, VxWorks
ThreadZephyr, POSIX (pthread), Windows
ProcessLinux (별도 메모리 공간)

본질은 같습니다. 독립 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

  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