Notice
Recent Posts
Recent Comments
Link
승코딩당당당
[RTOS] OSEK/Trampoline RTOS Task 스케줄링 실습 본문
이번 실습에서는 Trampoline RTOS 환경에서 여러 Task(Task1, Task2, TaskM)를 정의하고, Task 간 상태 변화와 스케줄링 동작을 확인하였다. 특히 ActivateTask, ChainTask, GetTaskState API를 활용하여 Task의 실행 흐름과 상태 전이를 관찰하는 데 초점을 맞췄다.
접근 방법
1. 주기 Task (TickPrint)
- Timer2 기반 Alarm을 통해 약 1초 주기로 실행
- 카운터 c를 증가시키며 로그 출력
- c == 0일 때 Task1을 활성화
if (c == 0) {
ActivateTask(Task1);
}
2. Task1 → Task2 흐름 구성
Task1 동작
- 실행 시작 로그 출력
- Task 상태 확인
- 3초 대기
- Task2 활성화
- 다시 상태 확인
- 3초 대기
- 종료 후 TaskM으로 전환 (ChainTask)
Task2 동작
- 실행 시작 로그 출력
- 상태 확인
- 3초 대기
- 종료 후 TaskM으로 전환
TaskM 동작
- Task1, Task2 상태 출력 후 종료
asw.c
#include "bsw.h"
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;
}
}
}
TASK(TaskM)
{
printState(Task1);
printState(Task2);
TerminateTask();
}
TASK(Task1)
{
TaskType id;
printfSerial("Task1 Begins...");
printState(Task1);
printState(Task2);
mdelay(3000);
ActivateTask(Task2);
printState(Task1);
printState(Task2);
mdelay(3000);
GetTaskID(&id);
printfSerial("Task ID = %d...", id);
printfSerial("Task1 Finishes...");
ChainTask(TaskM);
}
TASK(Task2)
{
TaskType id;
printfSerial("Task2 Begins...");
printState(Task1);
printState(Task2);
mdelay(3000);
GetTaskID(&id);
printfSerial("Task ID = %d...", id);
printfSerial("Task2 Finishes...");
ChainTask(TaskM);
}
conf.oil
/* Running the following example:
* just call goil a first time using the command line:
* goil --target=avr/arduino/uno --templates=../../../../goil/templates/ serial.oil
* you may have to update ARDUINO section (l.13) for your configuration.
*
*/
OIL_VERSION = "2.5" : "test" ;
CPU test {
OS config {
STATUS = STANDARD;
BUILD = TRUE {
TRAMPOLINE_BASE_PATH = "../..";
APP_NAME = "base";
APP_SRC = "bsw.cpp";
APP_SRC = "asw.c";
LDFLAGS = "-Wl,-Map=counter.map";
CPPCOMPILER = "avr-g++";
COMPILER = "avr-gcc";
LINKER = "avr-gcc";
ASSEMBLER = "avr-gcc";
COPIER = "avr-objcopy";
SYSTEM = PYTHON;
LIBRARY = serial;
};
SYSTEM_CALL = TRUE;
};
APPMODE stdAppmode {};
/* The following code block is for basic features of the labs */
COUNTER DefaultTimer {
SOURCE = TIMER2_OVF;
TICKSPERBASE = 1;
MAXALLOWEDVALUE = 2000;
MINCYCLE = 1;
};
ALARM TimerAlarm {
COUNTER= DefaultTimer;
ACTION = ACTIVATETASK {
TASK = TickPrint;
};
AUTOSTART = TRUE {
// note: no more than 255 => MAXALLOWEDVALUE in counter
ALARMTIME = 977;
CYCLETIME = 977; //activated each 1000 ms
APPMODE = stdAppmode;
};
};
TASK TickPrint {
PRIORITY = 20;
AUTOSTART = FALSE;
ACTIVATION = 1;
SCHEDULE = FULL;
STACKSIZE = 256;
};
/* The above code block is for basic features of the labs */
/* Fill up your code in here */
TASK Task1 {
PRIORITY = 1;
AUTOSTART = FALSE;
ACTIVATION = 1;
SCHEDULE = FULL;
STACKSIZE = 256;
};
TASK Task2 {
PRIORITY = 2;
AUTOSTART = FALSE;
ACTIVATION = 1;
SCHEDULE = FULL;
STACKSIZE = 256;
};
TASK TaskM {
PRIORITY = 3;
AUTOSTART = FALSE;
ACTIVATION = 1;
SCHEDULE = FULL;
STACKSIZE = 256;
};
};
bsw.cpp
#include "tpl_os.h"
#include "Arduino.h"
#include "bsw.h"
#define LEN_BUF 64
extern "C" {
void mdelay(unsigned long delay_ms)
{
unsigned long prev_ms = millis(), current_ms = millis();
unsigned long period_ms = 20, cnt = 0;
while (cnt < (delay_ms / period_ms)) {
current_ms = millis();
if (current_ms - prev_ms >= period_ms) {
cnt++;
prev_ms = millis();
}
}
}
void printfSerial(const char *fmt, ...)
{
char buf[LEN_BUF];
va_list args;
va_start(args, fmt);
vsnprintf(buf, LEN_BUF, fmt, args);
va_end(args);
Serial.print(buf);
}
} // extern "C"
/**
* configure TIMER2 to generate an interrupt
* each 1 ms
*/
void timerInit()
{
TCCR2A = 0;
TCNT2 = 0;
// clkio = 16MHz => clk = 250KHz
TCCR2B = (1 << WGM22) | (1 << CS22); // CTC and clk = Clkio/64
// interrupt each 1ms
OCR2A = 250 - 1;
TIMSK2 |= (1 << TOIE2);
interrupts();
}
void setup(void)
{
pinMode(13, OUTPUT);
Serial.begin(115200); //115200 bps, 8N1
printfSerial("\n...............\n");
printfSerial("...OS Starts...\n");
printfSerial("...............\n");
timerInit();
}
/* This task will be activated every second */
TASK(TickPrint)
{
static long c = -4;
if (c == 0) {
ActivateTask(Task1);
}
printfSerial("\n%4ld: ", c++);
TerminateTask();
}
실행 결과

1. OS 시작 및 Tick 증가
...OS Starts...
-4:
-3:
-2:
-1:
- TickPrint Task가 주기적으로 실행됨
- 아직 Task1은 실행되지 않음
2. Task1 실행 시작
0: Task1 Begins...0: running...1: suspended...
- Task1이 실행되며 RUNNING 상태
- Task2는 아직 SUSPENDED 상태
3. Task2 활성화 및 선점 발생
3: Task2 Begins...0: ready...1: running...
- Task1이 Task2를 Activate
- Task2(priority 2)가 Task1(priority 1)보다 우선순위 높음
👉 선점 발생
- Task1 → READY 상태
- Task2 → RUNNING 상태
4. Task2 종료 후 TaskM 실행
6: Task ID = 1...Task2 Finishes...
0: ready...1: suspended...0: running...1: suspended...
- Task2 종료 후 ChainTask(TaskM)
- TaskM 실행되며 상태 출력
5. Task1 재실행 및 종료
9: Task ID = 0...Task1 Finishes...
0: suspended...1: suspended...
- Task1이 다시 실행되어 마무리
- 모든 Task가 SUSPENDED 상태로 종료
비선점(Non-Preemptive) 스케줄링 동작 확인
앞선 실습에서는 SCHEDULE = FULL 설정을 통해 선점형 스케줄링 동작을 확인하였다.
이번에는 동일한 코드 구조에서 SCHEDULE = NON으로 변경하여 비선점 방식에서의 실행 흐름을 비교해보았다.
변경 사항
SCHEDULE = NON;
- Task1, Task2, TaskM 모두 비선점 방식으로 설정
- 실행 중인 Task는 종료 전까지 CPU를 계속 점유
TASK Task1 {
PRIORITY = 1;
AUTOSTART = FALSE;
ACTIVATION = 1;
SCHEDULE = NON;
STACKSIZE = 256;
};
TASK Task2 {
PRIORITY = 2;
AUTOSTART = FALSE;
ACTIVATION = 1;
SCHEDULE = NON;
STACKSIZE = 256;
};
TASK TaskM {
PRIORITY = 3;
AUTOSTART = FALSE;
ACTIVATION = 1;
SCHEDULE = NON;
STACKSIZE = 256;
};
실행 결과

1. Task1 실행 시작
0: Task1 Begins...0: running...1: suspended...
- Task1이 실행되며 RUNNING 상태
- Task2는 아직 SUSPENDED 상태
2. Task2 활성화 (하지만 즉시 실행되지 않음)
0: running...1: ready...
- Task1 내부에서 ActivateTask(Task2) 호출
- Task2는 READY 상태로 전이
하지만 여기서 중요한 점은,
👉 Task2가 READY 상태임에도 불구하고 실행되지 않음
3. Task1이 끝날 때까지 계속 실행
Task ID = 0...Task1 Finishes...
- Task1이 ChainTask() 또는 종료될 때까지 계속 실행됨
- 중간에 Task2로 전환되지 않음
👉 선점형과 가장 큰 차이 발생 지점
4. Task2 실행
Task2 Begins...0: suspended...1: running...
- Task1 종료 이후에야 Task2가 실행됨
- Task2가 RUNNING 상태로 전이
5. Task2 종료
Task ID = 1...Task2 Finishes...
0: suspended...1: suspended...
모든 Task가 종료되며 SUSPENDED 상태로 돌아감
선점 vs 비선점 핵심 비교
| 구분 | 선점 (FULL) | 비선점 (NON) |
| Task 전환 시점 | 즉시 | 현재 Task 종료 후 |
| 우선순위 반영 | 즉시 | 지연됨 |
| 실행 흐름 | 중간에 끊김 | 끝까지 실행 |
| Task2 실행 시점 | Activate 직후 | Task1 종료 후 |
'개발 > 임베디드' 카테고리의 다른 글
| [RTOS] RTOS에서 Mutex 동작 실습 및 이론 정리 (0) | 2026.04.28 |
|---|---|
| [RTOS] RTOS Scheduling과 OSEK 개념 정리 (0) | 2026.04.23 |
| [차량 통신] SDV 전환과 차량 통신 구조 정리 (CAN vs Ethernet) (0) | 2026.04.21 |
| [차량 통신] 차량 네트워크 구조 정리 (CAN, Ethernet, TCP/IP) (0) | 2026.04.16 |
| [차량 통신] CAN, FlexRay, LIN, SOME/IP (0) | 2026.04.14 |