STM32/STM32_RTOS

STM32 RTOS 알아보기 6편(Mutex)

원원 2025. 2. 16. 19:13

안녕하세요. 오늘은 세마포어와 뮤텍스에 대해 알아보겠습니다.
(xSemaphoreCreateMutex,xSemaphoreTake,xSemaphoreGive)

세마포어와 뮤텍스는 여러 태스크가 공유 자원에 안전하게 접근하도록 동기화하기 위해서 사용합니다.
이 글에서는 세마포어와 뮤텍스를 알기전에 알아야 할 지식을 알아보고 뮤텍스에대해 알아보겠습니다.

예를들어 아래의 코드에서 global_i는 TASK1과 TASK2가 동시에 접근하는 공유자원입니다.
이런경우 임계구역(Critical Section)이 발생하고, 레이스컨디션(Race Condition)문제가 발생할 수 있습니다.

void task(void *pvParameters);

int global_i=0;

void myTask()
{
  xTaskCreate(task, "Task1", 256, "TASK1", 3, NULL);
  xTaskCreate(task, "Task2", 256, "TASK2", 3, NULL);
}

void task(void *pvParameters)
{
  int local_i = 0;
  char *task_name;
  task_name = (char *)pvParameters;
  while(1)
  {
    local_i = global_i;
    local_i++;
    global_i = local_i;
    printf("%s %d \n",task_name, global_i);
  }
}

임계구역이란 여러 태스크가 동시에 접근할 수 있는 공유 자원을 수정하는 코드 영역을 의미합니다. 
아래의 코드가 임계구역입니다.

    local_i = global_i;
    local_i++;
    global_i = local_i;


레이스컨디션
이란 둘 이상의 태스크가 동시에 임계 구역을 실행하면서 예상하지 못한 결과가 발생하는 문제가 발생하는 상황을 의미합니다. 예를들어 Task1에서 local_++ 코드 다음에 global_i와 local_i코드가 같은지 비교했을때 같은 상황이 나올 수 있습니다.
local_++ 이후에  if((global_i == local_i) || end) 은 만족할수없는 상황입니다. 그러나 두 테스크가 임계구역을 동시에 접근하면서 만족하는 상황이 생길 수 있습니다.

void task(void *pvParameters);

int global_i=0;
int end = 0;

void myTask()
{
  xTaskCreate(task, "Task1", 256, "TASK1", 3, NULL);
  xTaskCreate(task, "Task2", 256, "TASK2", 3, NULL);
}

void task(void *pvParameters)
{
  int local_i = 0;
  char *task_name;
  task_name = (char *)pvParameters;
  while(1)
  {
    local_i = global_i;
    local_i++;
    if((global_i == local_i) || end)
    {
      printf("%s END\n",task_name);
      end = 1;
      vTaskDelay(1000);
      vTaskDelete(NULL);
    }
    global_i = local_i;
    printf("%s %d \n",task_name, global_i);
  }
}


공유 자원을 보호하기 위해 뮤텍스(Mutex)를 사용하겠습니다.
하나의 태스크가 뮤텍스를 획득하여 사용 중일 때, 다른 태스크는 뮤텍스를 얻을 때까지 Block 상태가 됩니다.
이후, 뮤텍스를 사용하던 태스크가 해제하면, 대기 중이던 다른 태스크가 뮤텍스를 획득하여 실행할 수 있습니다.

void task(void *pvParameters);

SemaphoreHandle_t mutex;

int global_i=0;
int end = 0;

void myTask()
{
  mutex = xSemaphoreCreateMutex();

  xTaskCreate(task, "Task1", 256, "TASK1", 3, NULL);
  xTaskCreate(task, "Task2", 256, "TASK2", 3, NULL);
}

void task(void *pvParameters)
{
  int local_i = 0;
  char *task_name;
  task_name = (char *)pvParameters;


  while(1)
  {
    if(xSemaphoreTake(mutex,portMAX_DELAY)== pdTRUE)
    {
      local_i = global_i;
      local_i++;
      if((global_i == local_i) || end)
      {
        printf("%s END\n",task_name);
        end = 1;
        vTaskDelay(1000);
        vTaskDelete(NULL);
      }
      global_i = local_i;
      printf("%s %d \n",task_name, global_i);
      xSemaphoreGive(mutex);
    }
    vTaskDelay(1);
  }
}

xSemaphoreCreateMutex로 뮤텍스를 생성하고 xSemaphoreTake로 뮤텍스를 획득합니다. 작업이 끝나면 xSemaphoreGive로 뮤텍스를 해제합니다.

다음 글에서는 세마포어에대한 예시를 알아보고 세마포어와 뮤텍스를 비교해보겠습니다.

소스코드(Commits: Mutex) 
https://github.com/yhunterr/STM32_RTOS_STUDY/commits/main/