본문 바로가기
Embedded/OSEK Real-Time OS

Race condition & Resource

by kjy1010 2024. 1. 21.

Race condition
Race Condition은 멀티스레드 또는 멀티태스킹 환경에서 여러 스레드 또는 태스크가 공유된 자원에 동시에 접근하려고 할 때 발생하는 문제를 나타냅니다. OSEK RTOS 환경에서도 race condition이 발생할 수 있습니다.

Race Condition이 발생하는 상황은 주로 다음과 같습니다:

  • 공유 자원에 동시 접근: 여러 태스크가 동시에 공유 변수, 자료구조, 메모리 영역 등에 접근할 때 발생합니다.
  • 스케줄링의 불확실성: 다양한 태스크들이 스케줄링에 의해 실행되는 순서가 예측하기 어려울 때 발생합니다.
  • 비원자적 연산 수행: 여러 단계로 이뤄진 연산 중 일부 단계만 수행되고 다른 태스크가 끼어들어 해당 연산을 완전히 수행하기 전에 결과를 읽는 경우 등이 해당됩니다.


Race Condition이 발생하면 예기치 않은 결과, 데이터 손상, 시스템의 불안정성 등의 문제가 발생할 수 있습니다. 이를 방지하기 위해 OSEK에서는 다양한 동기화 메커니즘을 제공합니다. 예를 들면, 뮤텍스(Mutex), 세마포어(Semaphore), 이벤트(Event) 등을 사용하여 공유 자원에 대한 접근을 조절하고 동기화를 유지할 수 있습니다.

실습을 통해 Race condition에 대해 이해하는 시간을 가지겠습니다.

Race condition 실습
실습을 위한 Task와 자원 접근 조건에 대해 말씀드리겠습니다.

  • Task1 = Low priority, AUTOSTART
  • Task2 = High priority, periodic, activation (1ms period)
  • 공유 자원 접근은 volatile unsigned long shared라는 전역변수를 통해 하겠습니다.
  • Task1 은 shared++를 100만번합니다.
  • Task2는 주기적으로 실행하며 shared++를 합니다.
  • Timer 주기를 1 msec로 설정합니다.


코드를 작성해보겠습니다.
bsw.c 파일을 수정하여 Timer주기를 변경하겠습니다.

#define TIMER_US 1000U /* 1 msec */


조건에 맞춰 asw.c 파일을 작성합니다.

#include "bsw.h"

volatile unsigned long shared = 0;

ISR2(TimerISR)
{
   IncrementCounter(counter1);
}

TASK(Task1){
      unsigned long i;
   printfSerial("Task1 Begins...\n");
   for(i = 0; i<1000000; i++){
      shared++;
   }
   printfSerial("Added 1000000 to shared\n");
   printfSerial("shared = %4lu\n", shared);
   printfSerial("Task1 finishes...");
   TerminateTask();
}

TASK(Task2){
	static unsigned long i=0;
	if(i<1000)
	{
		shared++;
	}
	else if(i==1000)
	{
		printfSerial("Added 1000 to shared\n");
	}
   i++;
   TerminateTask();
}


조건에 맞춰 OIL파일을 작성합니다.

CPU mySystem {
    OS myOs {
      EE_OPT = "OS_EE_APPL_BUILD_DEBUG";
      EE_OPT = "OS_EE_BUILD_DEBUG";

        USERESSCHEDULER = TRUE;
        CPU_DATA = AVR8 {
            MULTI_STACK = TRUE;
        };
        MCU_DATA = MEGA {
            MODEL = MEGA_328p;
        };
        LIB = ARDUINO {
            SDK_BOARD = UNO;
            VARIANT = CC {
                VERSION = "1.8.5";
            };
            STAND_ALONE = TRUE;        // Generate Arduino libraries
        };
        KERNEL_TYPE = OSEK {
            CLASS = ECC2;    // Default
        };
    };
    
    APPDATA myApp {
        APP_SRC = "bsw.cpp";
        APP_SRC = "asw.c";
    };
   
    COUNTER counter1{
       MINCYCLE = 1;
       MAXALLOWEDVALUE = 127;
       TICKSPERBASE = 1;
    };
       

    ALARM alarm1{
    	COUNTER = counter1;
    	ACTION = ACTIVATETASK{
    		    	TASK = Task2;
    };
    AUTOSTART = TRUE{
    	ALARMTIME = 0;
    	CYCLETIME = 1;
    };
   };
   
   
    TASK Task1{
       PRIORITY = 1;
       STACK = SHARED;
       SCHEDULE = FULL;
       AUTOSTART = TRUE;
       ACTIVATION = 1;
    };
    
    TASK Task2{
        PRIORITY = 2;
       STACK = SHARED;
       SCHEDULE = FULL;
       AUTOSTART = FALSE;
       ACTIVATION = 1;
    };

       ISR TimerISR{
       CATEGORY = 2;
       SOURCE = "TIMER1_COMPA";
    	};
    };


실행결과

위의 결과를 보면 shared의 값이 100536로 증가된 것을 확인 할 수 있습니다.
우리가 원하던 조건은 Task1 은 shared++를 100만번하고, Task2는 주기적으로 실행하며 shared++를 하는것 이였습니다.
이때 Task2는 1000만 돌아가도록 코드를 작성했었기에 shared의 값은 1001000이 되었어야 합니다.
Task가 공유된 자원에 동시에 접근하려고 할 때 발생하는 문제를 나타냅니다각했던 위의 상황이 Race condition상황이라 볼 수 있습니다.

OSEK resource
OSEK RTOS에서 “resource(리소스)“는 공유 자원에 대한 접근을 동기화하고, 경쟁 조건(Race Condition)을 방지하기 위한 동기화 메커니즘 중 하나입니다. 리소스는 주로 공유된 변수, 데이터 구조, 장치 또는 코드 영역과 같은 공유 자원을 나타냅니다.
리소스는 크게 두 가지 타입으로 나눌 수 있습니다

  • Internal Resource(내부 리소스): 태스크 내에서만 접근 가능한 리소스로, 해당 태스크의 코드 영역에 대한 접근을 제한합니다.
  • External Resource(외부 리소스): 여러 태스크 간에 공유되는 리소스로, 여러 태스크가 동시에 접근할 수 있습니다.


리소스를 사용하여 공유 자원에 대한 접근을 동기화할 때는 다음과 같은 OSEK API 함수들이 사용됩니다

  • GetResource(resourceName): 리소스를 획득(락)합니다. 이미 다른 태스크에 의해 획득된 리소스에 대해선 해당 태스크는 대기 상태로 전이됩니다.
  • ReleaseResource(resourceName): 획득한 리소스를 반납(언락)합니다.


Race condition을 OSEK의 Resource를 사용하여 해결해보는 실습을 진행해보겠습니다.

OSEK resource 실습
OIL 파일을 수정하여 resource를 사용할 수 있도록 수정하고 Task1에 resource를 추가해주겠습니다.


RESOURCE S1{
    	RESOURCEPROPERTY = STANDARD;
    };
   
    TASK Task1{
       PRIORITY = 1;
       STACK = SHARED;
       SCHEDULE = FULL;
       AUTOSTART = TRUE;
       ACTIVATION = 1;
       RESOURCE = S1;
    };


asw.c 파일에서 Task1과 Task2의 shared를 증가 시키는 부분 앞뒤로 resource를 사용 할 수 있도록 수정합니다.

TASK(Task1){
      unsigned long i;
   printfSerial("Task1 Begins...\n");
   for(i = 0; i<1000000; i++){
	   GetResource(S1);
      shared++;
      ReleaseResource(S1);
   }
   printfSerial("Added 1000000 to shared\n");
   printfSerial("shared = %4lu\n", shared);
   printfSerial("Task1 finishes...");
   TerminateTask();
}

TASK(Task2){
	static unsigned long i=0;
	if(i<1000)
	{
		GetResource(S1);
		shared++;
		ReleaseResource(S1);
	}
	else if(i==1000)
	{
		printfSerial("Added 1000 to shared\n");
	}
   i++;
   TerminateTask();
}


실행결과

'Embedded > OSEK Real-Time OS' 카테고리의 다른 글

Mutex  (0) 2024.01.21
Hook  (0) 2024.01.21
Alarm & Event  (0) 2024.01.20
Task  (0) 2024.01.20
OSEK project 기초  (0) 2024.01.20