인프런 강의 링크
홍영기(가일스쿨) 교수님 블로그 링크
졸업프로젝트로 FreeRTOS 기반 상용 RTOS인 'ESP-IDF'를 활용한 프로젝트를 진행한 뒤, 인프런의 FreeRTOS 강의 수강을 통해 내용을 확실히 정리했습니다. 이해한 내용을 최종적으로 정리하며 기록을 남기고자 글을 올립니다.
저작권을 최대한 존중하기 위해 홍영기 교수님의 강의자료와 실습자료는 일부라도 절대 공유하지 않으며, FreeRTOS 공식 레퍼런스 문서를 기반으로 작성합니다.
IPC를 사용하다보면 필연적으로 공유자원을 사용하고 싶은 욕구가 생긴다.
.
buttonCnt
라는 공유자원을 사용하고 있다.buttonCnt++
라는 단순한 한 줄 짜리 연산도 어셈블리어로 무려 5줄이나 차지하고 있다. 저 어셈블리 연산은 atomic 하지 않기 때문에 얼마든지 저 사이에 접근이 일어날 수 있다.buttonCnt == 100
이면 특정 동작을 수행하도록 조건문을 만들었는데, buttonCnt++
연산 도중 task B에 의해 한 번 더 증가해 buttonCnt = 101
이 됐다고 하자. 그럼 task A의 특정 동작은 수행될 수가 없다.∴ 따라서, critical section problem은 OS가 반드시 중요하게 다뤄야하는 문제다! Critical section에 대한 소유권은 반드시 단일 task가 독점하도록 보장해줘야 한다.
taskENTER_CRITICAL()
, taskEXIT_CRITICAL()
이라는 커널 API를 제공한다. 이 API는 인터럽트보다 우선순위가 높다!!저작권을 존중하므로 전체 실습 소스코드는 공유하지 않는다.
FreeRTOS의 critical section 보호 방법 중 하나인 taskENTER_CRITICAL
을 사용한다.
xSemaphoreCreateBinary()
, xSemaphoreCreateCounting()
xSemaphoreTake()
, xSemaphoreTakeFromISR()
xSemaphoreGive()
, xSemaphoreGiveFromISR()
vSemaphoreDelete()
0
과 1
값으로만 이뤄진 semaphore다. Critical section에 대해 p연산을 수행하면 semaphore가 0
이 되서 다른 task의 접근을 막는다. 접근을 요청한 task는 blocked 상태가 되며 들어갈 때 까지 기다린다. (무한정 기다리는 건 아니고, take 함수에서 기다리는 시간을 설정할 수 있다.)0
부터 사용자가 설정한 수까지 값을 가지는 semaphore다. 예를 들어 공유자원이 10칸짜리 배열일 때, semaphore를 10
으로 초기화하면, counting semaphore는 남은 공유자원의 개수를 의미한다. semaphore가 0
이 되면 해당 배열이 가득 찼음을 의미하며 접근을 요청한 task는 blocked 상태가 되며 기다린다.Clicked[]
give()
연산으로 semaphore 값 증가한다.take()
중인 task가 blocked에서 running이 되며 몇 번째 semaphore이고 현재 semaphore의 값이 무엇인지 출력한다. Sem번호(semaphore 값)
Clicked[12], [13], [14]
가 먼저 출력되는 것을 확인할 수 있다. 그럼 이제 semaphore가 0, 1, 2
로 늘었을 것이고, task는 연속해서 3번 take()
연산을 수행할 수 있다.(P.s. ISR에서 무거운 연산을 처리할 경우 버튼에 대한 인터럽트가 온전히 실행되지 않을 수 있다. 실제로 버튼을 굉장히 빠르게 5번 눌렀지만, 4번(11, 12, 13, 14)만 인식이 됐다. ISR은 최대한 작게 만들어야 다른 인터럽트나 task에 악영향을 최소한으로 만들 수 있다. ISR이 무겁고 속도가 느릴 수 밖에 없다면 뒤에서 배울 deferred interrupt processing 이라는 좋은 방법을 사용해서 해결할 수 있다.)
Mutex는 binary semaphore과 거의 같은 기능을 수행하지만, 중대한 차이가 하나 있다.
바로 우선순위 역전 현상을 예방해줄 메커니즘을 포함하고 있다는 점이다.
Semaphore는 critical section 및 공유자원에 대한 독점권을 보장하는 훌륭한 동기화 수단이지만, 치명적인 문제인 ‘우선순위 역전 현상’을 유발할 수 있다.
take()
연산을 수행해 critical section이 lock이 걸린다.take()
연산을 수행한다. 이때 semaphore가 0
이므로 task2는 blocked state로 진입한다.give()
연산으로 semaphore를 반환한다.우선순위 역전 현상을 해결하는 방법으로 FreeRTOS는 우선순위 상속(inheritance)을 사용한다.
give()
하자마자 다시 원래 우선순위로 돌아간다.