이번 주제인 mutual exclusion(상호 배제)는 여러 태스크가 같은 자원에 동시에 억세스 하지 못하도록 보장해 주는 메커니즘이다.
두개의 태스크가 동시에 같은 자원(여기서는 가상의 프린터 )를 사용하는 경우 태스크가 실행되는 도중에 RTOS에 의해 선점되면 출력 결과가 아래와 같은 식으로 되어 버린다.
Printer 1: +++++++++++Printer 2: -----------------------------
++++++++++++++++++
Printer 1: Printer 2: -----------------------------
+++++Printer 2: -----------------------------
++++++++++++++++++++++++
Printer 2: -----------------------------
PrintePrinter 2: -----------------------------
r 1: ++++++++Printer 2: -----------------------------
+++++++++++++++++++++
Printer 1: Printer 2: -----------------------------
+++++++++++++++++++++++++++++
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 1: +++++++++++++++Printer 2: -----------------------------
++++++++++++++
Printer 1: ++++++Printer 2: -----------------------------
+++++++++++++++++++++++
Printer 2: -----------------------------
위의 결과가 나온 코드이다.
#include <FreeRTOS_AVR.h>
#define MS(x) ((unsigned long)(x)/portTICK_PERIOD_MS)
void vPrint(void *);
void setup()
{
Serial.begin(115200);
xTaskCreate(vPrint, "Printer1", 200, (void *)"Printer 1: +++++++++++++++++++++++++++++", 1, NULL);
xTaskCreate(vPrint, "Printer2", 200, (void *)"Printer 2: -----------------------------", 2, NULL);
vTaskStartScheduler();
while (1) ;
}
void loop()
{
}
void vPrint(void *pvParameters)
{
unsigned char led_state = 0;
while (1) {
printer((char *)pvParameters);
vTaskDelay(MS(random(100)));
}
}
void printer(char *p)
{
while (*p != '\0') {
Serial.print(*p++);
delay(1);
}
Serial.println(" ");
}
하지만 원하는 것은 한 태스크가 프린터로 출력하는 동안 다른 태스크가 방해하지 못하게 하는 것이다.
Printer 1: +++++++++++++++++++++++++++++
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 2: -----------------------------
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 1: +++++++++++++++++++++++++++++
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 2: -----------------------------
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 2: -----------------------------
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 1: +++++++++++++++++++++++++++++
Printer 2: -----------------------------
Printer 1: +++++++++++++++++++++++++++++
Printer 1: +++++++++++++++++++++++++++++
이런식으로 출력되게 하기 위해 한 태스크가 프린터를 사용하는 동안 다른 태스크는 프린터에 접근하지 못하고 대기하게 하기 위해 MUTEX를 사용한다.
#include <FreeRTOS_AVR.h>
#define MS(x) ((unsigned long)(x)/portTICK_PERIOD_MS)
SemaphoreHandle_t xMutex;
void vPrint(void *);
void setup()
{
Serial.begin(115200);
xMutex = xSemaphoreCreateMutex();
if (NULL != xMutex) {
xTaskCreate(vPrint, "Printer1", 200, (void *)"Printer 1: +++++++++++++++++++++++++++++", 1, NULL);
xTaskCreate(vPrint, "Printer2", 200, (void *)"Printer 2: -----------------------------", 2, NULL);
vTaskStartScheduler();
} else {
Serial.println("Create mutex failed.");
}
while (1) ;
}
void loop()
{
}
void vPrint(void *pvParameters)
{
unsigned char led_state = 0;
while (1) {
xSemaphoreTake( xMutex, portMAX_DELAY); // block forever until the semaphore is given
// Critical section start
printer((char *)pvParameters);
// Critical section end
xSemaphoreGive( xMutex );
vTaskDelay(MS(random(500)));
}
}
void printer(char *p)
{
while (*p != '\0') {
Serial.print(*p++);
delay(1);
}
Serial.println(" ");
}
위의 코드에서 빨간색으로 표시된 부분을 Critical Section(임계영역)이라고 한다. 여러 태스크가 공유하지만 동시에 억세스하면 안되는 자원을 억세스 하는건 critical section 내에서만 해야 한다. C.S.는 xSemaphoreTake()로 시작하고 xSemaphoreGive()로 끝난다. 한 태스크가 C.S. 내에 있는 코드를 실행하고 있는 동안 다른 태스크가 C.S. 안으로 들어가려고 하면 xSemaphoreTake()에서 블럭되어 현재 C.S. 내에서 실행중이 태스크가 빠져 나오면서 xSemaphoreGive()를 호출할 때 까지 sleep 상태로 대기하게 되므로 동시에 두개 이상의 태스크가 C.S. 내로 진입할 수 없게 된다.
1. CS에 아무 태스크도 진입하지 않은 상태
위의 예제에서는 태스크간에 공유하는 자원으로 프린터를 예로 들었지만 그 이외에 서로 공유하는 전역변수들 역시 억세스 할 때는 C.S. 내에서 해야 한다.
그리고 태스크가 C.S. 내에 들어 있는 동안 그 C.S.에 진입하려고 하는 태스크는 블럭되어 실행되지 못하므로 ISR과 마찬가지로 C.S. 내에서도 가능한 빨리 작업을 마치고 빠져 나와야 한다.
댓글 없음:
댓글 쓰기