Mutex
OSEK RTOS에서 “Mutex(뮤텍스)“는 공유 자원에 대한 접근을 동기화하기 위한 동기화 메커니즘 중 하나입니다. 뮤텍스는 Mutual Exclusion(상호 배제)의 약자로, 한 번에 하나의 태스크만이 특정 공유 자원에 접근할 수 있도록 보장합니다. 이를 통해 경쟁 조건(Race Condition)을 방지하고 데이터의 일관성을 유지할 수 있습니다.
뮤텍스를 사용하는 OSEK API 함수는 주로 다음과 같습니다:
• GetResource(mutexName): 뮤텍스를 획득(락)합니다. 이미 다른 태스크에 의해 획득된 뮤텍스에 대해선 해당 태스크는 대기 상태로 전이됩니다.
• ReleaseResource(mutexName): 획득한 뮤텍스를 반납(언락)합니다.
실습을 진행하며 mutex에 대해 이해해보는 시간을 가지겠습니다.
Mutex 실습
Mutex를 사용하기 위해 관련 파일을 추가해보겠습니다.
기존에 사용하던 프로젝트에 mutex.h파일을 추가해줍니다.
ifndef MUTEX_H_
#define MUTEX_H_
#define LOCKED 1
#define UNLOCKED 0
typedef struct _MutexType {
int flag;
EventMaskType event;
TaskType waiting_task;
} MutexType;
void InitMutex(MutexType *mutex, EventMaskType event);
void GetMutex(MutexType *mutex);
void ReleaseMutex(MutexType *mutex);
#endif /* MUTEX_H_ */
mutex.h 를 include한 mutex.파일도 추가해줍니다.
#include "ee.h"
#include "bsw.h"
#include "mutex.h"
void InitMutex(MutexType *mutex, EventMaskType event)
{
mutex->flag = UNLOCKED;
mutex->waiting_task = 0;
mutex->event = event;
}
void GetMutex(MutexType *mutex)
{
if (mutex->flag == LOCKED) {
printfSerial(" --> BLock");
GetTaskID(&(mutex->waiting_task));
WaitEvent(mutex->event);
}
mutex->flag = LOCKED;
}
void ReleaseMutex(MutexType *mutex)
{
if (mutex->flag == LOCKED) {
mutex->flag = UNLOCKED;
if (mutex->waiting_task != 0) {
SetEvent(mutex->waiting_task, mutex->event);
}
}
}
이제 mutex를 사용할 준비가 끝났습니다.
mutex 동작 확인을 위한 Task를 다음 조건에 맞춰 작성해보겠습니다.
- TaskH = High priority
- TaskL = Low priority
- TaskH는 c == 5 에 Activate, S1 get try 하며, 3초 실행 후 S1 release
- TaskL는 c == 0 에 Activate, 1초 실행 후 S1 get try 하며, 5초 실행 후 S1 release
asw.c에 mutex를 선언하고, TimerISR에서 mutex를 초기화하고, Task activation 할 수 있도록 하겠습니다.
#include "bsw.h"
#include "mutex.h"
MutexType s1;
TASK(TaskH){
printfSerial("<TaskH Begins>");
mdelay(1000);
printfSerial("TaskH : Try Lock(s1). ");
GetMutex(&s1);
printfSerial("TaskH : Get Lock(s1). ");
mdelay(3000);
printfSerial("TaskH Release Lock(s1). ");
ReleaseMutex(&s1);
mdelay(1000);
printfSerial("<TaskH end>");
TerminateTask();
}
TASK(TaskL){
printfSerial("<TaskL Begins>");
mdelay(1000);
printfSerial("TaskL : Try Lock(s1). ");
GetMutex(&s1);
printfSerial("TaskL : Get Lock(s1). ");
mdelay(3000);
printfSerial("TaskL Release Lock(s1). ");
ReleaseMutex(&s1);
mdelay(1000);
printfSerial("<TaskL end>");
TerminateTask();
}
ISR2(TimerISR)
{
static long c =-5;
printfSerial("\n%4ld: ",++c);
if(c == -4){
InitMutex(&s1,Event1);
}
else if(c==0){
ActivateTask(TaskL);
}
else if(c ==5){
ActivateTask(TaskH);
}
}
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";
APP_SRC = "mutex.c";
};
EVENT Event1 { MASK = AUTO;};
TASK TaskH{
PRIORITY = 2;
STACK = PRIVATE{
SIZE = 256;
};
SCHEDULE = FULL;
AUTOSTART = FALSE;
ACTIVATION = 1;
EVENT = Event1;
};
TASK TaskL{
PRIORITY = 1;
STACK = PRIVATE{
SIZE = 256;
};
SCHEDULE = FULL;
AUTOSTART = FALSE;
ACTIVATION = 1;
EVENT = Event1;
};
ISR TimerISR{
CATEGORY = 2;
SOURCE = "TIMER1_COMPA";
};
COUNTER counter1{
MINCYCLE = 1;
MAXALLOWEDVALUE = 127;
TICKSPERBASE = 1;
};
};
실행결과
코드를 실행하면 위와 같은 결과를 확인 할 수 있습니다.
순서를 확인해보겠습니다.
1. 실습조건에 맞춰 TaskL이 시작되고 1초 후 mutex가 동작하여 Lock이 걸립니다.
2. TaskL이 Lock 걸리면, 1초뒤 TaskH가 시작되며, TaskH가 Lock이 걸립니다.
3. TaskH가 Lock이 걸리면 TaskL의 Lock이 Realse되고 TaskH의 Lock을 Get합니다.
4. 4초뒤 TaskH가 Release되며, TaskH와 TaskL을 순차적으로 종료합니다.
'Embedded > OSEK Real-Time OS' 카테고리의 다른 글
Race condition & Resource (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 |