지노랩 /JinoLab

한 눈에 정리하는 FreeRTOS Task State - BLOCKED vs. SUSPENDED 본문

임베디드 시스템/RTOS

한 눈에 정리하는 FreeRTOS Task State - BLOCKED vs. SUSPENDED

지노랩/JinoLab 2025. 6. 21. 11:03

RTOS를 쓰다 보면
태스크가 안 돌아요…” “언제 깨어나는 거죠?” 같은 질문을 매번 하게 됩니다.
이번 글에서는 가장 헷갈리는 BLOCKED 상태와 SUSPENDED 상태를 중심으로,
Delay API·동기화 오브젝트까지 실전 위주로 정리했습니다.


1. 4대 최상위(Task) 상태 복습

상태 의미 전이(Transition)

Running CPU 독점 실행 중 ❌ (오직 1 개)
Ready 실행 조건 만족 ➜ CPU 할당만 대기 ▸ 스케줄러가 우선순위 기준 선택
Blocked 이벤트·시간·자원 을 기다리며 “잠시 쉼” ▸ 이벤트/타임아웃 발생 → Ready
Suspended 사용자 호출로 ‘중단’ – 커널도 안 깨움 ▸ vTaskResume() 으로만 Ready

포인트

  • Blocked ↔ Ready 는 자동 전환
  • Suspended ↔ Ready 는 수동 전환

2. BLOCKED : 조건 충족 때까지 “자동 대기”

2-1. 진입 방법

API / 오브젝트 대기 조건

vTaskDelay( ticks ) 설정한 Tick 수 경과
vTaskDelayUntil( &xLastWake, period ) 정확한 주기 유지
xQueueReceive( xQ, …, ticks ) 큐에 데이터 OR 타임아웃
xSemaphoreTake( xSem, ticks ) 세마포어 가용 OR 타임아웃

→ 호출 즉시 Running → Blocked
→ CPU 사이클 0 % 사용 (즉, 저전력)

/* 100 ms 동안 CPU 반납 */
vTaskDelay( pdMS_TO_TICKS(100) );

2-2. 깨어나는 시점

  • 이벤트 충족 (큐에 데이터, 세마포어 획득 등)
  • 타임아웃 – 지정 시간이 지나도 이벤트 없으면 Ready로 자동 복귀

Tip
타임아웃=0 을 주면 “영원히” 기다릴 수 있음 (portMAX_DELAY)


3. SUSPENDED : “커널도 모른 척, 네가 깨워”

3-1. 진입 / 복귀 API

vTaskSuspend( xHandle );         // 어떤 상태든 → Suspended
vTaskResume( xHandle );          // 실행권 회복 (Ready)
vTaskResumeFromISR( xHandle );   // ISR 내 복귀
  • 자동 타임아웃 없음
  • 다른 태스크/ISR이 명시적으로 Resume 해 줘야만 Ready 전환

3-2. 언제 쓰나?

사용 시나리오 이유

펌웨어 OTA 중 특정 태스크 ‘잠깐 OFF’ 업데이트 충돌 방지
모드 전환(예: 저속↔고속) 불필요한 태스크 완전 정지
테스트/디버그용 태스크 임시 중단 로직 단순화

4. BLOCKED vs. SUSPENDED 비교표

항목 Blocked Suspended

진입 방식 Delay·큐·세마포어 등 커널 API vTaskSuspend() (수동)
깨어남 이벤트 or 타임아웃 자동 vTaskResume() 수동
타임아웃 O X
용도 지연·동기화 모드 전환·임시 중단
Ready로 가는 길 커널이 판단 사용자/ISR 호출

5. Delay API 빠르게 쓰는 법

5-1. vTaskDelay()

/* 500 ms 슬립 */
vTaskDelay( pdMS_TO_TICKS(500) );
  • 지연 후 실행 시점이 Tick 해상도에 ±1 틱 오차
  • 간단-최소 코드에 적합

5-2. vTaskDelayUntil()

void vTask(void *arg)
{
    TickType_t xLast = xTaskGetTickCount();
    const TickType_t period = pdMS_TO_TICKS(10);

    for(;;)
    {
        /* 정확히 10 ms 간격으로 실행 */
        vTaskDelayUntil( &xLast, period );
        doJob();
    }
}
  • 이전 실행 시점 기준, 정확한 주기 유지
  • 주기성 요구(센서 폴링, PID 루프 등)에 필수

6. 실전 팁 & 주의사항

  1. 큐/세마포어는 “0 Tick 타임아웃”으로 폴링하지 말 것
    Blocked 상태를 활용해 CPU ‘놀지’ 않게 한다.
  2. 지연 대신 Suspend?
    • 지연(Delay)은 반드시 다시 깨어남 → 주기성에 좋음
    • Suspend는 이벤트 없이 영구 중단될 수 있으니 로직 설계 주의
  3. Idle Hook + 낮은 Tick Rate로 저전력 최적화
  4. Race Condition 디버깅: eTaskGetState()로 태스크 상태 실시간 로깅

마무리

  • Blocked = “조건 충족 시 자동 복귀”
  • Suspended = “_사용자_가 깨울 때까지 휴면”

두 상태를 적절히 활용하면

  • CPU 부하 ↓
  • 전력 소모 ↓
  • 코드 가독성 ↑

다음 글에서는 큐·세마포어·이벤트그룹을 이용한 _실전 동기화 패턴_을 다루겠습니다.
궁금한 점은 댓글로 남겨 주세요!