1) Mutexes
뮤텍스는 공유 자원에 대한 접근을 제어하기 위한 동기화 도구, 뮤텍스는 한 번에 하나의 태스크만 자원에 접근할 수 있도록 보장하는 역할을 한다.
뮤텍스는 공유 리소스에 대한 액세스를 제어하는 데 사용되는 특수한 유형의 바이너리 세마포어이다
바이너리 세마포어와 다른 점 : 뮤텍스는 우선순위 역전 현상을 예방해줄 메커니즘을 포함
xSemaphoreHandle = xSemaphoreCreateMutex(); // 뮤텍스 생성
if (xSemaphoreTake(xMutex, portMAX_DELAY)) { // 뮤텍스 획득
// 자원 사용
xSemaphoreGive(xMutex); //뮤텍스 반환
}뮤텍스는 우선순위 역전 문제 방지를 위해 우선순위 상속 메커니즘을 제공
xSemaphoreCreateMutex(); 에서 NULL 반환 시 힙 영역 부족하다는 뜻

뮤텍스를 태스크 A에서 사용중일 경우, 뮤텍스에 접근한 태스크 B는 Blocked state로 전환

- 뮤텍스가 반환되면 B의 Blocked state 종료

2) Binary Semaphores
0 과 1로만 이뤄진 semaphore다. Critical section에 대해 p연산을 수행하면 semaphore가 0이 되어서 다른 task의 접근을 막는다.
접근을 요청한 task는 blocked 상태가 되며 들어갈 때 까지 기다린다.
- take 함수에서 기다리는 시간을 설정
xSemaphoreGive();xSemaphoreTake();
3) Priority Inheritance (우선순위 상속)
일시적으로 나의 우선순위(높은)를 주어서 Inversion 문제를 해결

해결 방법
- 리소스를 가진 LP의 우선순위를 HP 수준으로 일시적으로 끌어올려 줌
- 따라서 MP가 HP보다 먼저 실행되지 않게 한다.
4) Deadlock (or Deadly Embrace)
둘 이상의 태스크가 서로 가진 자원을 기다리며 무한 대기 상태에 빠지는 것
- Task A는 Mutex X를 점유하고 Mutex Y를 기다림
- Task B는 Mutex Y를 점유하고 Mutex X를 기다림
- → 서로 양보하지 않아서 stuck
해결 방법
- 리소스를 항상 같은 순서로 획득하도록 설계
- 타임아웃 설정
- 재귀 뮤텍스(Recursive Mutex) 사용 (아래 설명)
5) Recursive Mutexes (재귀적 뮤텍스)
xSemaphoreHandle mutex = xSemaphoreCreateRecursiveMutex();
xSemaphoreTakeRecursive(mutex);
xSemaphoreTakeRecursive(mutex); // 같은 태스크이기 때문에 가능 (재귀)
xSemaphoreGiveRecursive(mutex);
xSemaphoreGiveRecursive(mutex); // 두번 반환해야 완전 해제- 하나의 태스크가 같은 뮤텍스를 여러 번 획득 할 수 있도록 허용
- 일반 Mutex는 한 번만 획득 가능 → 중첩 호출 시 문제 발생
- 재귀 뮤텍스는 획득 횟수를 기억하고, 같은 수만큼 반환해야 진짜 반환됨
6) Mutexes and Task Scheduling
- Mutex는 공유 자원 보호뿐 아니라 태스크 스케줄링에 간접적 영향도 줌
- 예: 높은 우선순위 태스크가 뮤텍스를 기다리면 블로킹 상태로 빠지고, 그 사이 낮은 우선순위 태스크가 실행될 수 있음
→ 뮤텍스는 단순한 동기화 수단이 아니라, RTOS 스케줄링의 흐름까지 영향을 줌
7) Gatekeeper Tasks

- 특정 리소스에 대한 단독 소유권을 갖는 태스크
- 다른 태스크는 해당 리소스를 직접 접근하지 않고, Gatekeeper를 통해 간접 접근
- Example 21 : gatekeeper 태스크를 사용하도록 vPrintString() 재구현(tick hook 함수를 이용해 200틱마다 메세지 전송)
- Gatekeeper는 큐(xPrintQueue) 를 통해 문자열 포인터를 수신
- 출력 요청 태스크는 printf 직접 호출 대신, 메시지를 큐에 전송
- Gatekeeper는 메시지를 수신하면 printf로 출력, 이후 다시 대기
- 대부분 시간은 xQueueReceive() 에서 Blocked 상태로 대기
- Tick Hook
- vApplicationTickHook() 사용 → 200틱마다 문자열 전송
- 사용 전 FreeRTOSConfig.h에서 configUSE_TICK_HOOK = 1 설정 필요
Tick Hook 주의사항
- Tick ISR 컨텍스트에서 실행됨
- 코드 짧게 작성, 스택 사용 최소화
- FromISR 계열 함수만 사용 가능
- pxHigherPriorityTaskWoken → 사용하지 않으면 NULL로 설정 가능
{
char *pcMessageToPrint;
for( ;; )
{
xQueueReceive( xPrintQueue, &pcMessageToPrint, portMAX_DELAY );
printf( "%s", pcMessageToPrint );
fflush( stdout );
}
} static void prvPrintTask( void *pvParameters )
{
int iIndexToString;
const TickType_t xMaxBlockTimeTicks = 0x20;
iIndexToString = ( int ) pvParameters;
for( ;; )
{
xQueueSendToBack( xPrintQueue, &( pcStringsToPrint[ iIndexToString ] ), 0 );
vTaskDelay( ( rand() % xMaxBlockTimeTicks ) );
}
} void vApplicationTickHook( void )
{
static int iCount = 0;
iCount++;
if( iCount >= 200 )
{
xQueueSendToFrontFromISR( xPrintQueue, &( pcStringsToPrint[ 2 ] ), NULL );
iCount = 0;
}
} static char *pcStringsToPrint[] =
{
"Task 1 ****************************************************\r\n",
"Task 2 ----------------------------------------------------\r\n",
"Message printed from the tick hook interrupt ##############\r\n"
};
QueueHandle_t xPrintQueue;
int main( void )
{
xPrintQueue = xQueueCreate( 5, sizeof( char * ) );
if( xPrintQueue != NULL )
{
xTaskCreate( prvPrintTask, "Print1", 1000, ( void * ) 0, 1, NULL );
xTaskCreate( prvPrintTask, "Print2", 1000, ( void * ) 1, 2, NULL );
xTaskCreate( prvStdioGatekeeperTask, "Gatekeeper", 1000, NULL, 0, NULL );
vTaskStartScheduler();
}
for( ;; );
}'RTOS' 카테고리의 다른 글
| freeRTOS Resource Management(1)-Critical Sections and Suspending the Scheduler (0) | 2026.02.26 |
|---|---|
| freeRTOS Task management(2) -라운드로빈, 우선순위스케줄링과 Not Running State (0) | 2025.05.25 |
| freeRTOS Task Management(1)-CMSIS, Task 생성 (0) | 2025.05.25 |