임계영역?
여러 스레드가 동기화되지 않은 상태로 공유 자원에 동시에 접근한다면, 당연하게도 문제가 발생한다.
atomic한 연산은 어셈블리 코드 한 줄이다. 그런데 우리가 사용하는 C언어에서 거의 모든 연산은 어셈블리 코드 한 줄로 끝나지 않는다. 단순히 a에 1을 더하는 행위도 a의 값을 메모리에서 꺼내와 레지스터에 로드하고, 1을 더하고, 다시 메모리에 저장하는 과정을 거친다.
만약 여러 스레드에서 동시에 1을 더하려고 시도한다면? A스레드에서 a를 로드한 시점에 B스레드에서도 a를 로드하고, 각자 1을 더하고 저장하면 의도 상으로는 2가 더해져 있어야 하지만 같은 값에 1을 더하고 저장한 것이므로 1이 더해져 있을 뿐일 것이다.
따라서 우리는 공유 자원에 대한 임계 영역 관리가 필요하다.
FreeRTOS의 임계 영역 문제 해결
인터럽트 중단
SYSTICK 인터럽트를 막으면 선점이나 컨텍스트 스위칭이 일어나지 않는다! 즉, 막혀있는 동안 실행되는 코드는 atomic함이 보장된다는 것이다.
taskENTER_CRITICAL()과 taskEXIT_CRITICAL()이라는 커널 API를 사용해 이를 구현할 수 있다. 이들은 인터럽트보다 우선순위가 높기 때문에 실행 도중에 선점 당하지 않는다.
세마포어 or 뮤텍스 사용
운영체제 시간에 지겹게 다룬 그 것... 멀티스레드 환경에서 임계 영역을 해결할 때 가장 일반적인 방법이라 볼 수 있다.
임계 영역에 대한 lock - unlock 연산을 통해 atomic한 환경을 구성해준다.
물론 위 방법에 비해 오버헤드가 발생할 수도 있지만, 만약 atomic한 환경의 연산량이 많은 편이라면, 당연하게도 multitasking 능력은 이쪽이 더 좋다.
세마포어
세마포어는 동기화 및 공유 자원 보호, 이벤트 전달 용도로 사용된다. Binary Semaphore와 Counting Semaphore가 존재하는데, 말 그대로 0과 1로 구성돼있냐, 그 이상의 수를 가질 수 있냐는 뜻이다.
여기서 세마포어의 값은 공유자원의 개수를 의미한다. 즉, 0 이상일 때 세마포어를 하나 쥐고 접근할 수 있다. 물론 임계 구역을 나올 때에는 세마포어를 반환해야한다.
초기화는 세마포어 핸들 자료형 SemaphoreHandle_t을 가진 변수에 xSemaphoreCreateBinary() 또는 xSemaphoreCreateCounting()로 할당한다. 차이는 함수명 그대로이다.
획득의 경우 xSemaphoreTake(semaphoreHandle, TickType_t t)인데, t 값은 틱 단위 대기시간으로 portMAX_DELAY로 줌으로서 무한정 대기할 수 있다. 블로킹 모드로 동작할 수 있으며, 세마포어를 획득했다면 pdTRUE를 반환한다.
xSemaphoreTakeFromISR(semaphoreHandle, &xHigherPriorityTaskWoken)은 ISR 컨텍스트에서 세마포어를 얻기 위해 사용된다. 인터럽트에서 실행되기에 블로킹 모드로 동작하지 않으며, 호출 후 컨텍스트 스위칭이 필요한지 여부를 xHigherPriorityTaskWoken으로 반환한다.
xSemaphoreTake(semaphoreHandle), xSemaphoreTakeFromISR(semaphoreHandle, &xHigherPriorityTaskWoken)으로 세마포어를 반환할 수 있다.
세마포어를 삭제할 때에는 vSemaphoreDelete(semaphoreHandle)을 사용한다.
뮤텍스
뮤텍스는 binary semaphore와 거의 같은 기능을 수행하지만, 우선순위 역전 현상을 예방할 수 있는 기능이 추가된다.
우선순위 역전이란, 우선순위가 높은 task가 우선순위가 낮은 task를 기다리는 현상을 뜻한다. 이를 해결하기 위해 freeRTOS는 우선순위 상속을 사용한다. 낮은 우선순위의 task는 동일한 mutex를 요청한 우선순위가 높은 task의 우선순위를 상속받고, 임계 영역이 끝나면 다시 원래 우선순위로 돌아간다. 상속으로 역전 현상을 완전히 해결할 수는 없지만, 빈도를 낮추고 적정한 time bound 내에 해결되도록 유도한다.
뮤텍스는 xSemaphoreCreateMutex()로 생성되고, 나머지 함수는 세마포어의 task 버전 함수와 같다. (심지어 핸들러의 자료형도 같다)
'현생 > 임베디드' 카테고리의 다른 글
FreeRTOS - Event Flag Group (0) | 2024.08.08 |
---|---|
FreeRTOS - Interrupt (0) | 2024.08.07 |
FreeRTOS - Naming Rule & Task (0) | 2024.08.05 |
임베디드 OS - RTOS란 무엇일까 (0) | 2024.08.04 |
임베디드 개발자에게 필요한 전기 기본 상식 (0) | 2024.08.03 |