RTOS 기반 설계에서 리소스를 공유하기 위한 세마포르 또는 뮤텍스

 

모든 작업이 단일 주소 공간에 존재할 때 작업은 데이터를 쉽게 공유할 수 있으며 글로벌 변수, 포인터, 버퍼, 연결된 목록, 링 버퍼 등을 참조할 수 있다. 데이터 공유는 작업 간 정보 교환을 간소화하지만 경쟁이나 데이터 손상을 방지하려면 각 작업은 데이터에 대한 독점적인 액세스 권한을 가져야 한다.

소프트웨어로 구현된 간단한 일 기준 회귀를 수행하는 작업을 예로 들어보자. 이 작업은 시, 분, 초만 추적하지만 일, 월, 월, 연도 및 윤년을 지원하도록 쉽게 확장할 수 있다. 실제로 이러한 모듈은 필자의 임베디드 시스템 빌딩 블록(Embedded Systems Building Blocks)[3] 책의 6장에서 찾을 수 있다.

Minutes을 0으로 설정한 직후에 이 작업이 다른 작업에 의해 선점(인터럽트에 의해 트리거 됨)되었고 다른 작업은 TimeOfDayTask()보다 더 중요했다고 가정해보자. 이제 이 우선순위가 높은 작업이 현재 시간을 알고 싶어할 경우(즉, 읽기) 어떤 일이 일어날지 상상해 보자. Hours 변수가 인터럽트 전에 증가하지 않았기 때문에 우선 순위가 높은 작업은 시간을 잘못 읽을 수 있으며, 이 경우 시간이 한 시간씩 틀릴 수 있다.

TimeOfDayTask() 작업에 대한 변수를 업데이트하는 코드는 가능한 선점이 있을 때마다 모든 변수를 분할할 수 없거나 원자적으로 처리해야 한다. 시간 변수는 공유 리소스로 간주되며 해당 변수에 액세스하는 모든 코드는 모든 변수에 대한 배타적 액세스 권한을 가져야 한다. RTOS는 세마포르와 상호 배제 세마포르(즉, 뮤텍스)를 통해 이러한 서비스(예: API)를 제공한다. 이제 어떤 것을 언제 사용해야 하는지 살펴보자.

RTOS 기반 시스템에서 작업은 목록 2에 표시된 대로 세마포어 또는 뮤텍스를 사용해 공유 리소스를 래핑해야 한다. 사용할 정확한 API는 사용 중인 RTOS에 따라 다르다.

 

uint8_t  Hours;

uint8_t  Minutes;

uint8_t  Seconds;

 

void TimeOfDayTask (void)

{

    Seconds = 0;

    Minutes = 0;

    Hours   = 0;

    while (1) {

        // Delay (i.e., sleep) task for 1 second     

        if (Seconds >= 59) {

            Seconds  = 0;

            if (Minutes >= 59) {

                Minutes  = 0;

                if (Hours >= 23) {

                    Hours  = 0;

                } else {

                    Hours++;

                }

            } else {

                Minutes++;

            }

        } else {

            Seconds++;

        }

    }

}

     

목록 . 리소스 공유 보호 기능이 없는 Time-of-Day Clock


uint8_t  Hours;

uint8_t  Minutes;

uint8_t  Seconds;

 

void TimeOfDayTask (void)

{

    Seconds = 0;

    Minutes = 0;

    Hours   = 0;

    while (1) {

        // Delay (i.e., sleep) task for 1 second

        Acquire Semaphore (or mutex);  // API depends on your RTOS   

        if (Seconds >= 59) {

            Seconds  = 0;

            if (Minutes >= 59) {

                Minutes  = 0;

                if (Hours >= 23) {

                    Hours  = 0;

                } else {

                    Hours++;

                }

            } else {

                Minutes++;

            }

        } else {

            Seconds++;

        }

Release Semaphore (or mutex);  // API depends on your RTOS   

    }

}

목록 2. 리소스 공유 보호 기능이 없는 단순한 Time-of-Day Clock

세마포르(Semaphores)

너무 많은 세부 사항을 다루지 않은 경우, 세마포르는 리소스에 액세스하기 위한 작업을 진행하기 전에 획득해야 하는 이다(목록 2에서 살펴보았듯이). 작업이 공유 리소스에 액세스한 후에는 이 를 해제해야 한다 예상한 바와 같이 실제로 물리적 키를 얻는 것이 아니라 가상 키를 얻는다. 세마포르를 사용하려면 먼저 초기화해야 한다. 응용프로그램은 각각 다른 리소스를 보호하는 세마포르를 원하는 만큼 선언할 수 있다. 세마포르에는 바이너리와 카운팅, 두 가지 유형이 있다. 

 

바이너리 세마포르에는 단일 키가 포함된다. 이 경우 세마포르 값은 키가 다른 작업에서 사용 중인 경우 0, 키를 사용할 수 있는 경우 1로 표시된다. 일 기준 회귀 구현에서는 응용프로그램에 단일 시계만 필요하므로 이진 세마포르를 사용한다. 시간대는 일 기준 회귀의 오프셋으로 쉽게 처리할 수 있다.

사용할 수 있는 동일한 리소스가 여러 개 있으므로 카운팅 세마포르에는 여러 개의 키가 포함된다. 카운팅 세마포르는 모든 키가 작업에 할당된 경우 0, 키가 사용 가능한 경우 >0의 값을 가진다. 카운팅 세마포르를 사용하는 예는 동일한 버퍼의 버퍼 풀 관리이다. 버퍼 풀에서 버퍼가 필요할 때마다 RTOS API를 호출하여 키를 획득한다. 사용 가능한 경우(세마포르 값은 > 0) 버퍼 할당자가 호출자에게 버퍼를 제공한다. 사용 가능한 버퍼가 없으면 호출 작업은 버퍼 중 하나를 소유한 작업 중 하나에 의해 버퍼가 해제될 때까지 일시 중단된다(대기 작업의 우선 순위가 가장 높다고 가정). 

따라서, 세마포르를 사용하려면 다음을 수행해야 한다:

  • 바이너리 세마포르가 필요한지, 카운팅 세마포르가 필요한지 결정
  • 세마포르 초기화 바이너리 세마포르를 1로 초기화하고 카운팅 세마포르를 N(공유할 수 있는 동일한 리소스의 수)으로 초기화한다.

  • 리소스에 액세스해야 할 때마다 RTOS API를 호출하여 세마포르를 획득(즉, 키 가져오기)하고 완료되면 RTOS API를 호출해 세마포르를 해제한다.

일반적으로, 세마포르는 무한 우선순위 역전의 대상이기 때문에 사용하지 않는 것이 좋다! 대신 뮤텍스를 사용해야 한다. 필자의 µC/OS-III 책[1]에서, 특히 13장 섹션 13.3.5에서 우선순위 반전에 대한 자세한 설명을 찾을 수 있다.

상호 배제 세마포르(일명 뮤텍스)

뮤텍스는 바이너리 세마포르의 특수한 유형이며 무한 우선순위 반전을 제거하는 데 사용된다. 세마포르와 마찬가지로 뮤텍스는 획득/해제를 시도하기 전에 초기화되어야 한다. 초기화는 RTOS에서 제공하는 API를 통해 수행된다. 

우선 순위가 낮은 작업이 우선 순위가 높은 리소스도 액세스해야 하는 경우, RTOS는 뮤텍스 소유자가 가능한 한 빨리 리소스에 액세스하고 리소스를 해제할 수 있도록 우선 순위가 낮은 작업의 우선 순위를 우선 순위가 높은 작업의 우선 순위와 일치하도록 변경한다. 리소스가 해제되면 소유자의 우선순위가 원래 우선순위로 복원된다. 뮤텍스를 해제하면 RTOS는 리소스에 대한 액세스를 제공하는 대기 중인 우선 순위가 높은 작업으로 즉시 전환된다.

세마포르 또는 뮤텍스를 사용해야 합니까?

일반적으로 공유 리소스에 액세스할 때 뮤텍스를 기본적으로 사용하고 신호 전달 메커니즘(다른 기사 대상)으로 세마포르 사용을 예약해야 한다. 

그러나 세마포르는 세마포르 소유자의 우선순위를 변경할 수 없고 세마포르가 해제될 때 다시 복원할 수 없기 때문에 세마포르 API의 실행 시간은 일반적으로 뮤텍스보다 훨씬 빠르다. 이러한 이유로, 공유 리소스를 공유하는 데 관련된 모든 작업의 우선 순위가 동일하거나 세마포어를 사용하는 작업에 충족할 데드라인이 없거나 우선순위 역전의 가능성을 신경 쓰지 않는 경우 세마포르를 사용해도 전혀 문제가 없다.

고려해야 할 또 다른 중요한 요소는 세마포르와 뮤텍스 API가 잠재적으로 수백 개의 CPU 주기를 소비하므로 이상적으로는 공유 리소스에 액세스하는 실행 시간이 이러한 RTOS API의 실행 시간보다 길어야 한다는 점이다. 즉, 몇 개의 변수만 업데이트하는 경우 인터럽트를 비활성화하고 변수를 업데이트한 다음 인터럽트를 다시 활성화하는 것을 고려할 수 있다. 이 경우에도 물론 인터럽트를 비활성화/활성화할 수 있다고 가정한다. 일부 RTOS는 사용자 모드(권한 없음)에서 응용프로그램 코드를 실행하므로 인터럽트 비활성화를 허용하지 않을 수 있다. 해당 RTOS 문서를 참조한다.

 

참고문헌:

[1] µC/OS-III, The Real-Time Kernel:

µC/OS-III, The Real-Time Kernel, and the Freescale Kinetis ARM Cortex-M4, Jean J. Labrosse, 978-0-9823375-2-3

µC/OS-III, The Real-Time Kernel, and the Infineon XMC4500, Jean J. Labrosse, 978-1-9357722-0-0

µC/OS-III, The Real-Time Kernel, and the NXP LPC1700, Jean J. Labrosse, 978-0-9823375-5-4

µC/OS-III, The Real-Time Kernel, and the Renesas RX62N, Jean J. Labrosse, 978-0-9823375-7-8

µC/OS-III, The Real-Time Kernel, and the Renesas SH7216, Jean J. Labrosse, 978-0-9823375-4-7

µC/OS-III, The Real-Time Kernel, for the STM32 ARM Cortex-M3,                  Jean J. Labrosse, 978-0-9823375-3-0

µC/OS-III, The Real-Time Kernel, and the Stellaris MCUs,
Jean J. Labrosse, 978-0-9823375-6-1

[2] Weston Embedded Book Downloads

https://weston-embedded.com/micrium-books

[3] Embedded Systems Building Blocks, Complete and Ready-to-Use Modules in C

Jean J. Labrosse

ISBN 978-0879306045

 

저자 소개

본 기사는 RTOS 개발 애플리케이션에 관한 시리즈의 일부입니다.

Jean Labrose는 높은 인지도를 가진 uC/OS-II와 uC/OS-III 커널의 저자이자 Micrium의 설립자로, 임베디드 소프트웨어의 uC/라인의 발전에 적극적으로 관여하고 있습니다.

Jean은 풍부한 경험과 임베디드 시스템 시장에 대한 깊은 이해를 바탕으로 Weston Embedded Solutions의 수석 조언자 및 컨설턴트로 재직하고 있으며, 현재 RTOS 제품의 향후 보다 발전된 제안을 마련하는데 기여하고 있습니다. Weston Embedded Solutions는 Micrium 코드베이스에서 파생된 매우 안정적인 Cesium RTOS 제품군의 지원 및 개발을 전문으로 합니다. 

Jean.Labrosse@Weston-Embedded.com.

 
jean_labrosse.jpg