[FreeRTOS] Critical Section (임계 영역)

 

# Critical Section

공유 자원을 사용 중인 함수내의 일부 혹은 전체 영역.

공유 자원이라 함은 전역 변수 또는 하드웨어 주변장치 가 될 수 있다.

따라서 공유 자원에 접근해서 조작을 하는 중간에 컨텍스트 스위칭이 발생하면 데이터의 무결성이 깨지게 된다.

int 전역변수;

void Task1(void)
{
	...
    
    /* CRITICAL SCTION BEGIN */
    
    전역변수++; -> // 공유 자원에 접근하여 조작하는 코드
    코드에서는 1줄이지만, 실제로는 아래처럼 여러줄의 어셈블리 코드로 이루어져있다.
    
    1. 전역변수 메모리주소에서 값을 읽어오는 라인, 
    2. 메모리에서 값을 읽어 레지스터에 할당하는 라인,
    3. 레지스터에서 값을 조작 (증가연산) 하는 라인,
    4. 레지스터의 값을 다시 메모리에 Write하는 라인
    
    /* CRITICAL SECTION END */
    ...

}

void Task2(void)
{
	...
    
    /* CRITICAL SCTION BEGIN */
    
    // 공유 자원에 접근하여 조작하는 코드
    
    전역변수++; -> // 공유 자원에 접근하여 조작하는 코드
    
    /* CRITICAL SECTION END */
    ...

}



# 재 진입 (Reentrancy)

다수의 Task에서 호출하여 사용 할 수 있기 위해서 해당 함수는 재 진입 가능 하도록 작성 되어져야 함.  그렇다면 재 진입 가능 하다는 것은 무엇을 의미할까?

단순히 함수 호출을 통해 함수 Entry Point에 진입하는게 아니라,

위 예제 코드에서처럼, C 코드 한줄은 여러줄의 어셈블리 코드로 이루어져있는데, 어셈블리어 실행 중간에 태스크 스위칭이 발생하더라도 문제가 없음을 의미 한다.


재 진입 함수를 만드는 방법은 다음과 같다.

  • Task에서 공유자원에 접근하지 않도록 함
  • 오직 한 Task에서 하나의 자원을 독점적으로 사용
    • 만약 다른 Task 에서 해당 자원을 컨트롤 해야 할 일이 있다면, IPC를 통해 이벤트 메시지를 사용하여 자원에 대한 권한이 있는 Task에게 요청을 하는 방법을 사용하자


# 상호배제 ( Ciritical Section 제어 방법 )

1. 인터럽트 중단

int 전역변수;


void Task1(void)
{
    taskENTER_CRITICAL(); // 인터럽트 금지

    전역변수++; /* CRITICAL SECTION */

    taskEXIT_CRITICAL() ; // 인터럽트 활성화
}

void Task2(void)
{
    taskENTER_CRITICAL(); // 인터럽트 금지

    전역변수++; /* CRITICAL SECTION */

    taskEXIT_CRITICAL() ; // 인터럽트 활성화
}


주의사항 : 인터럽트를 금지시키므로, Tick 인터럽트도 발생하지 않게 되므로, Blocking API : vTaskDelay( ) 같은 커널 API를 사용하면 시스템이 멈출 수도 있음.

void Task1(void)
{
    taskENTER_CRITICAL(); // 인터럽트 금지

    전역변수++; /* CRITICAL SECTION */
    vTaskDelay(1);

    taskEXIT_CRITICAL() ; // 인터럽트 활성화
}


2. 스케쥴링 중단 ( FreeRTOS 에서는 지원하지 않음 )


3. 세마포어 사용

 - Blocking으로 인한 오버헤드 발생

 - 교착상태 (DeadLock) 발생 위험

void Task(void)
{
	...
    
    세마포어 LOCK
    	공유 자원 접근 /* CRITICAL SECTION */
    세마포어 Un-Lock
    ...
}


이 블로그의 인기 게시물

Crusader Kings 3 - Converting DNA code using Portrait Editor