Firmware & RTOS/FreeRTOS & Real-time Scheduling

FreeRTOS 세마포어(Semaphore) vs 뮤텍스(Mutex) 차이점과 올바른 사용법

임베디드 친구 2025. 1. 14. 08:34
반응형

임베디드 시스템에서 여러 태스크가 동시에 실행될 때, 공유 리소스에 한꺼번에 접근하면 데이터가 깨지는 문제가 발생합니다. 이를 방지하기 위해 FreeRTOS는 세마포어(Semaphore)와 뮤텍스(Mutex)라는 동기화 메커니즘을 제공합니다.

비슷해 보이지만 용도가 전혀 다른 이 두 개념의 차이점과 CMSIS-RTOS v2 기준의 실전 사용법을 완벽히 정리해 드립니다.

Generated by Gemini AI.


1. Semaphore vs Mutex: 핵심 차이점

가장 큰 차이는 '소유권(Ownership)'과 '우선순위 상속' 유무에 있습니다.

구분 Semaphore (세마포어) Mutex (뮤텍스)
주 목적 태스크 간 신호 전달(Signaling) 공유 리소스 보호(Locking)
소유권 없음 (누구나 Release 가능) 있음 (Acquire한 태스크만 Release 가능)
핵심 특징 카운팅 방식을 통해 리소스 수량 관리 가능 우선순위 상속으로 우선순위 역전 방지
비유 식당의 번호표 (빈 자리 알림) 화장실 열쇠 (한 명만 사용)

2. Semaphore 사용법: 태스크 간 동기화

세마포어는 특정 이벤트가 발생했음을 다른 태스크에 알릴 때(Signaling) 주로 사용합니다.

핵심 API (CMSIS-RTOS v2)

  • osSemaphoreNew: 세마포어 생성
  • osSemaphoreAcquire: 세마포어 획득 (P 연산)
  • osSemaphoreRelease: 세마포어 해제 (V 연산)

동기화 코드 예제

Task 1이 데이터를 준비하면 Task 2가 이를 처리하도록 동기화하는 구조입니다.

C
 
osSemaphoreId_t sem_id;

void Task1_Sender(void *argument) {
    for (;;) {
        // 이벤트 발생! 신호를 보냄
        osSemaphoreRelease(sem_id); 
        osDelay(1000);
    }
}

void Task2_Receiver(void *argument) {
    for (;;) {
        // 신호가 올 때까지 무한 대기
        if (osSemaphoreAcquire(sem_id, osWaitForever) == osOK) {
            // 이벤트 처리 로직
        }
    }
}

3. Mutex 사용법: 리소스 보호와 우선순위 상속

뮤텍스는 UART 통신이나 공유 메모리처럼 한 번에 한 태스크만 사용해야 하는 리소스를 보호할 때 사용합니다.

우선순위 역전(Priority Inversion) 방지

뮤텍스는 세마포어와 달리 우선순위 상속(Priority Inheritance) 기능을 지원합니다. 낮은 우선순위 태스크가 리소스를 쥐고 있을 때 높은 우선순위 태스크가 요청하면, 일시적으로 하위 태스크의 우선순위를 높여 작업을 빨리 끝내게 돕습니다.

리소스 보호 코드 예제

C
 
osMutexId_t mutex_id;

void Critical_Resource_Task(void *argument) {
    for (;;) {
        // 뮤텍스 획득 (리소스 잠금)
        if (osMutexAcquire(mutex_id, osWaitForever) == osOK) {
            // 공유 리소스(예: 전역 변수, UART) 사용
            printf("Resource Accessing...\n");
            
            // 뮤텍스 반환 (잠금 해제)
            osMutexRelease(mutex_id);
        }
        osDelay(100);
    }
}

4. 개발자 주의사항 (SEO 팁)

  1. 데드락(Deadlock) 주의: 두 태스크가 서로의 리소스를 기다리며 무한 대기에 빠지지 않도록 Acquire 순서를 일치시켜야 합니다.
  2. v1 vs v2 명칭 확인: 본 가이드는 최신 표준인 CMSIS-RTOS v2 기준입니다. 만약 구형 프로젝트(v1)라면 osSemaphoreWait 대신 osSemaphoreAcquire를 사용했는지 확인하세요.
  3. 적절한 도구 선택: 단순히 "이벤트가 발생했다"는 신호만 필요하다면 가벼운 Binary Semaphore를, "데이터 무결성"이 중요하다면 Mutex를 선택하는 것이 정석입니다.

결론

임베디드 환경에서 동기화 메커니즘을 잘못 선택하면 디버깅하기 매우 까다로운 Race Condition이나 Priority Inversion 문제에 직면하게 됩니다. 오늘 정리한 Semaphore와 Mutex의 특성을 이해하고 프로젝트의 목적에 맞게 적용해 보시기 바랍니다.


글이 도움이 되셨다면 공감과 댓글 부탁드립니다! 여러분은 실무에서 어떤 동기화 문제를 겪어보셨나요?

반응형