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

Hook

by kjy1010 2024. 1. 21.

Hook
OSEK RTOS에서 “hook”은 시스템 이벤트가 발생할 때 호출되는 사용자 정의 함수를 나타냅니다. OSEK에서 hook 함수는 시스템의 다양한 이벤트 포인트에서 실행되어 사용자가 특정 동작을 수행하도록 하는 확장성을 제공합니다. OSEK 표준은 다양한 유형의 훅을 정의하고 있습니다.

1. Startup Hook:
• 시스템이 초기화될 때 호출되는 훅입니다.
• 사용자가 초기화 코드를 추가하거나 특정 동작을 수행하도록 하는 데 사용됩니다.
2. Error Hook:
• 오류가 감지되면 호출되는 훅입니다.
• 시스템 에러 처리를 위해 사용자가 특정 코드를 추가할 수 있습니다.
3. PreTaskHook:
• 각 태스크가 실행되기 전에 호출되는 훅입니다.
• 태스크 실행 전에 특정 동작이 필요한 경우 사용됩니다.
4. PostTaskHook:
• 각 태스크가 실행된 후에 호출되는 훅입니다.
• 태스크 실행 후 특정 동작이 필요한 경우 사용됩니다.
5. Shutdown Hook:
• 시스템이 종료되기 전에 호출되는 훅입니다.
• 사용자가 종료 전에 정리 코드를 추가하거나 특정 동작을 수행하도록 하는 데 사용됩니다.
6. Protection Hook:
• 시스템의 크리티컬 섹션 진입 및 이탈 시 호출되는 훅입니다.
• 사용자가 크리티컬 섹션에서 추가적인 동작을 수행하도록 하는 데 사용됩니다.

Hook은 사용자가 시스템의 특정 이벤트에 대한 동작을 제어하고 확장할 수 있도록 하는 중요한 메커니즘입니다.
사용자가 특정 이벤트에 대한 동작을 추가할 필요가 있는 경우 해당 Hook을 구현하여 시스템 동작을 확장할 수 있습니다.

실습을 통해 Hook의 사용법에 대해 이해하는 시간을 가지겠습니다.
이전 포스팅의 OIL파일을 수정하여 Hook을 설정합니다.

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
        };
    STARTUPHOOK = TRUE;
    SHUTDOWNHOOK = TRUE;
    PRETASKHOOK = TRUE;
    POSTTASKHOOK = TRUE;
    };

    
    APPDATA myApp {
        APP_SRC = "bsw.cpp";
        APP_SRC = "asw.c";
    };
    
    TASK Task1{
       PRIORITY = 1;
       STACK = SHARED;
       SCHEDULE = FULL;
       AUTOSTART = FALSE;
       ACTIVATION = 1;
    };
    
    TASK Task2{
        PRIORITY = 2;
       STACK = SHARED;
       SCHEDULE = FULL;
       AUTOSTART = FALSE;
       ACTIVATION = 1;
    };

       ISR TimerISR{
       CATEGORY = 2;
       SOURCE = "TIMER1_COMPA";
    };
    COUNTER counter1{
       MINCYCLE = 1;
       MAXALLOWEDVALUE = 127;
       TICKSPERBASE = 1;
    };
    ALARM alarm1{
       COUNTER = counter1;
       ACTION = ACTIVATETASK{
          TASK = Task1;
       };
       AUTOSTART = TRUE{
          ALARMTIME = 5;
          CYCLETIME = 10;
       };
    };
    ALARM alarm2{
       COUNTER = counter1;
       ACTION = ACTIVATETASK{
          TASK = Task2;
       };
       AUTOSTART = TRUE{
          ALARMTIME = 5;
          CYCLETIME = 20;
       };
    };

};


asw.c 파일을 수정하여 printState 함수를 추가하고, Task2를 수정하여 printState 함수를 사용 할 수 있도록 합니다.
또한 각각의 Hook routiens를 구현합니다.

#include "bsw.h"

TASK(Task1){
   printfSerial("Task1 Begins...");
   mdelay(3000);
   printfSerial("Task1 Finishes...");
   TerminateTask();
}

TASK(Task2){
   printfSerial("Task2 Begins...");
   mdelay(3000);
   printfSerial("Task2 Finishes...");
   TerminateTask();
}

void printState(TaskType id){
	TaskStateType state;

	if(GetTaskState(id,&state)==E_OK){
		switch (state){
		case SUSPENDED:
			printfSerial("%d: suspended...",id);
			break;
		case READY:
			printfSerial("%d: ready...",id);
			break;
		case WAITING:
			printfSerial("%d: waiting...",id);
			break;
		case RUNNING:
			printfSerial("%d: running...",id);
			break;
		}
	}
}
ISR2(TimerISR)
{
   static long c = -4;
   IncrementCounter(counter1);
   printfSerial("\n%4ld",c++);
}

void StartupHook(void)
{
	printfSerial("..StartupHook..\n");

}

void ShutdownHook(StatusType Error)
{
	printfSerial("..ShutdownHook..\n");
	printState(Task1);
	printState(Task2);
}

void PreTaskHook(void)
{
	TaskType id;
	GetTaskID(&id);
	printfSerial("[PreTaskHook(%d)]",id);
	printState(Task1);
	printState(Task2);
}

void PostTaskHook(void)
{
	TaskType id;
		GetTaskID(&id);
		printfSerial("[PostTaskHook(%d)]",id);
		printState(Task1);
		printState(Task2);
}


실행결과

코드를 실행한 결과입니다.
우선 OS가 시작되면 시스템이 초기화 되기 전에 호출되는 StartupHook이 호출됩니다.
4초후 Task가 시작되기전에 호출되는 PreTaskHook이 호출되고 Task2가 시작됩니다.
Task2가 끝나고 나면, Task가 실행된 후 호출되는 PostTaskHook이 호출되며, Task1이 시작되기 전에 PreTaskHook이 다시 호출됩니다.
Task1이 끝나면 PostTaskHook이 다시 호출됩니다. 이를 반복하는 것을 확인 할 수 있습니다.

Error handling
이제 Error handling를 Hook을 사용하여 하는 것을 진행해보도록 하겠습니다.

OIL파일을 수정하여 ErrorHook을 추가하고 이전에 활성화했던 hook을 비활성화 하겠습니다.

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
        };
    STARTUPHOOK = FALSE;
    SHUTDOWNHOOK = FALSE;
    PRETASKHOOK = FALSE;
    POSTTASKHOOK = FALSE;
    ERRORHOOK = TRUE;
    USEGETSERVICEID = TRUE;
    USEPARAMETERACCESS = TRUE;
    };


asw.c 파일을 수정하여 ErrorHook을 추가하고, ErrorHook이 동작하는지 확인하기 위해 Timer에 테스트케이스를 추가하겠습니다.

ISR2(TimerISR)
{
   static long c = -4;
   TaskStateType s;
   if(c==5)
   {
	   GetTaskState(30,&s);
   }
   IncrementCounter(counter1);
   printfSerial("\n%4ld",c++);
}

void ErrorHook(StatusType error)
{
	printfSerial("[ErrorHook: error = %d, service = %d, TaskID = %d]",error,OSErrorGetServiceId(),OSError_GetTaskState_TaskID());
}


실행결과

실행결과입니다. 타이머가 9초의 틱이 지나면 Error Hook을 호출하며 해당 State를 출력하는 것을 확인 할 수 있습니다.

Deadline miss
Osek RTOS에서 “Deadline Miss”는 실시간 시스템에서 발생하는 중요한 개념 중 하나입니다. Deadline은 특정 작업이 완료되어야 하는 시간을 나타내며, Deadline Miss는 해당 작업이 정해진 Deadline 내에 완료되지 못한 상황을 의미합니다.
실시간 시스템에서는 각 작업(Task)에 대해 실행을 완료해야 하는 시간 제한이 있습니다. 이 시간 제한을 Deadline이라고 하며, Deadline을 넘어서 작업이 완료되면 시스템 동작이 불안정해질 수 있습니다.
Deadline Miss가 발생하는 상황은 주로 다음과 같은 경우입니다:

  • 우선순위 역전(Priority Inversion): 높은 우선순위의 태스크가 낮은 우선순위의 태스크에 의해 블록되는 상황에서, 중간 우선순위의 태스크가 높은 우선순위의 태스크의 자원을 점유하게 되면 Deadline Miss가 발생할 수 있습니다.
  • 부적절한 스케줄링: 작업의 우선순위, 스케줄링 알고리즘 등이 부적절하게 설정되어 있는 경우에도 Deadline Miss가 발생할 수 있습니다.
  • 자원 경쟁(Race Condition): 공유 자원에 대한 경쟁이 발생하는 상황에서 여러 작업이 서로의 진행을 차단하고 Deadline이 놓치는 경우가 있을 수 있습니다.


Deadline Miss가 발생하면 실시간 시스템에서 시간 제한을 준수할 수 없게 되어 심각한 문제를 일으킬 수 있습니다. 이러한 상황을 방지하기 위해 적절한 우선순위 설정, 스케줄링 알고리즘의 선택, 자원 사용의 조절 등이 중요합니다. Deadline Miss를 최소화하고 실시간 시스템을 안정적으로 운영하기 위해서는 전반적인 시스템 디자인과 구현에 신경을 써야 합니다.

위에서 사용한 ErrorHook을 사용하여 Deadline miss 사례를 살펴보겠습니다.
asw.c파일에서 타이머 내용과 Task1을 수정하여 Task1에서 Deadline miss를 발생해보겠습니다.

TASK(Task1){
   TaskType id;
   printfSerial("Task1 Begins...");
   mdelay(7000);
   printfSerial("Task1 Finishes...");
   TerminateTask();
}

ISR2(TimerISR)
{
	static long c = -4;
	IncrementCounter(counter1);
	printfSerial("\n%4ld: ",c++);
}


실행결과


실행결과에서 ErrorHook의 내용을 살펴보면 <service = 82>라는 내용을 볼 수 있습니다.
이 내용은 kernel Internal Error라는 뜻입니다. Activation수가 초과되어 Task1의 Deadline Miss가 발생하였음을 확인 할 수 있습니다.

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

Mutex  (0) 2024.01.21
Race condition & Resource  (0) 2024.01.21
Alarm & Event  (0) 2024.01.20
Task  (0) 2024.01.20
OSEK project 기초  (0) 2024.01.20