OSEK 태스크(Task)와 ISR(Interrupt Service Routine)은 VDX의 두 가지 실행 단위다. Task는 OS가 제어하는 주기·이벤트 기반 실행 단위, ISR은 하드웨어 인터럽트로 트리거되는 핸들러. 둘 다 OIL(OSEK Implementation Language)로 정적 선언한다.
Processing Levels
OSEK은 실행 문맥을 우선순위 계층으로 분리한다.
| Level | 설명 |
|---|---|
| ISR Category 1 | 최상위, OS가 관여하지 않음 |
| ISR Category 2 | OS가 관리하는 인터럽트 핸들러 |
| Task | OS 스케줄러가 관리하는 실행 단위 |
| OS Background | (아이들) |
Task
선언 (C + OIL)
TASK(TaskL1) { // Task ID
... // Task Body
TerminateTask(); // End of Task (반드시 호출)
}TASK TaskL1 {
PRIORITY = 1; // 우선순위 (큰 값이 높음)
STACK = SHARED; // 공유 스택 사용
SCHEDULE = FULL; // FULL: preemptable, NON: non-preemptable
AUTOSTART = FALSE; // 자동 시작 여부
ACTIVATION = 1; // 최대 큐잉 활성화 횟수
};| OIL 속성 | 의미 |
|---|---|
PRIORITY | 숫자 값이 클수록 우선순위 높음 |
STACK | SHARED 또는 별도 할당 |
SCHEDULE | FULL = 선점 가능, NON = 비선점 (→ 실시간 스케줄링 선점 정책 참고) |
AUTOSTART | TRUE면 시스템 시작 시 자동 활성화 |
ACTIVATION | 큐에 쌓일 수 있는 최대 활성화 횟수 |
Task 상태 머신
OSEK Task는 running / ready / waiting / suspended 4-상태를 가진다. Basic Task는 waiting 상태를 사용하지 않는다.
| State | 의미 |
|---|---|
running | CPU가 Task에 할당되어 명령 수행. 한 번에 하나의 Task만 이 상태에 있을 수 있음 |
ready | 실행 가능하지만 CPU 미할당. 스케줄러가 이 집합에서 다음 실행 Task 선택 |
waiting | (Extended Task 전용) 특정 event가 발생할 때까지 동작 정지 |
suspended | 휴식 상태. activate되어야 Ready로 진입 |
상태 전이:
| 전이 | 계기 |
|---|---|
activate | ActivateTask 등 system service로 Task가 Ready로 진입 |
start | 스케줄러가 Ready 집합에서 Task를 선택해 Running으로 |
preempt | 스케줄러가 다른 Task 실행을 결정해 Running → Ready |
terminate | Task 본문이 TerminateTask로 종료 (또는 에러로 강제 종료) |
wait | (Extended) WaitEvent 호출로 Running → Waiting. Context 저장 필요 |
release | (Extended) Waiting Task에 기다리던 event가 발생해 Ready로 복귀 |
Basic Task vs Extended Task
| 항목 | Basic Task | Extended Task |
|---|---|---|
| Waiting 상태 | 불허 | 허용 |
| 전형적 코드 | { ... ; TerminateTask(); } | { ... ; WaitEvent(event); ... ; TerminateTask(); } |
| Event 사용 | ✗ | ✓ (WaitEvent로 이벤트 대기) |
| Conformance | BCC1 / BCC2 / ECC (모두 지원) | ECC1 / ECC2에서만 지원 |
Task API
| API | 용도 |
|---|---|
ActivateTask(TaskID) | Task를 Ready 상태로 (Suspended → Ready) |
TerminateTask() | 현재 Running Task를 Suspended로 |
ChainTask(TaskID) | 다른 Task를 activate하면서 자신은 terminate (원자적) |
Schedule() | 명시적으로 스케줄링 수행 (Non-preemptive Task가 자발 양보) |
GetTaskID(TaskRefType) | 현재 Running Task의 ID 조회 |
GetTaskState(TaskID, TaskRefType state) | Task ID의 현재 state(Running/Ready/Waiting/Suspended) 조회 |
모든 API는 StatusType을 반환한다. E_OK가 정상, E_OS_LIMIT은 ACTIVATION 한도 초과 등 — 전체 코드는 OSEK Error Handling 참고.
Context Switching
Context = MCU가 현재 실행 중인 Task 상태 — PC, SP, 범용 레지스터 등 Task 재개에 필요한 값의 집합. 선점이 일어나면 OS는 다음을 수행한다.
- Context Save — Running Task의 레지스터 상태를 메모리에 저장
- Context Load — 다음 실행 Task의 저장된 Context를 MCU 레지스터에 복원
스위치 시점은 Task 유형에 따라 다르다.
- Basic Task — preempt 시 Save, 다시 running 진입 시 Load
- Extended Task — 위에 더해
wait시에도 Save, release 이후 running 진입 시 Load
→ 개념의 일반론은 컨텍스트 스위치 참고.
Event — Extended Task 동기화
용도 — Extended Task 간 실행 순서를 맞추는 메커니즘. 예: Task A가 Task B의 연산 결과를 필요로 할 때, 결과 준비 event까지 wait.
Event 규칙
- Event wait은 Extended Task에서만 가능 (Waiting 상태를 지원해야 하므로)
- Event set은 Basic Task·ISR2에서도 가능
- Task가 waiting 상태로 들어가기 전에 이미 set된 event를 기다리면 바로 running 유지 (waiting으로 가지 않음)
ClearEvent는 event를 소유한 Task만 호출 가능 (event의 대기 주체)
API
| API | 동작 |
|---|---|
SetEvent(TaskID, EventMask) | 지정 Task의 event set. Task가 해당 event를 waiting 중이면 Ready로 |
ClearEvent(EventMask) | 호출 Task 자신의 event flag clear |
GetEvent(TaskID, EventMaskRef) | Task의 모든 event 상태 bitmap 반환 |
WaitEvent(EventMask) | 현재 Extended Task를 Waiting으로. 대기 event가 이미 set이면 즉시 복귀 |
3-Step 사용 예
Task A priority > Task B priority. Task A가 Event_A를 기다리고 Task B가 이를 발생시키는 시나리오.
Step 1) Task A: WaitEvent(Event_A)
→ Task A Running → Waiting, Ready Queue 다음 Task B가 Running
Step 2) Task B: SetEvent(TaskA, Event_A)
→ Task A에 event flag set, Task A Waiting → Ready
→ (Full preemptive) 스케줄링 발생, Task A가 Task B를 선점
Step 3) Task A 내에서: ClearEvent(Event_A)
→ Event flag 삭제. 다음 WaitEvent가 다시 block 가능해짐
※ Clear 하지 않으면 다음 WaitEvent는 즉시 return (이미 set 상태)OIL 선언
Event 자체는 이름과 mask만 설정하고, 실제 매핑은 Task의 OIL에서 수행한다. Task 하나에 여러 event가 있을 수 있으므로 mask로 구분.
EVENT Event_A {
MASK = AUTO; // 또는 비트 위치 지정
};
TASK TaskA {
EVENT = Event_A; // 이 Task가 사용하는 event 매핑
/* ... */
};ISR (Interrupt Service Routine)
인터럽트로 트리거되는 함수. OSEK은 두 가지 카테고리를 구분한다.
| 항목 | ISR Category 1 | ISR Category 2 |
|---|---|---|
| 시스템 콜 | 불허 (어떤 OS 서비스도 사용 불가) | 허용 (예: ActivateTask, CounterTick) |
| OS 인식 | 무시 | OS가 인식 |
| OIL 기술 | 불필요 | 필요 |
| 오버헤드 | 낮음 / 빠름 | 높음 / 느림 |
| 이식성 | 마이크로프로세서 간 이식성 없음 | 상대적으로 이식 가능 |
TerminateTask 호출 | — | 불필요 (ISR은 복귀로 종료) |
ISR2 선언 (C + OIL)
ISR(myISR) {
// ISR code
// no need to call TerminateTask()
}ISR MyISR {
CATEGORY = 2;
PRIORITY = 1; // 인터럽트 우선순위
STACKSIZE = 32768;
SOURCE = SIGUSR1; // 인터럽트 소스
};우선순위 계층
OSEK은 실행 컨텍스트 사이에 고정 우선순위 계층을 강제한다.
Interrupt level > Logical level for scheduler > Task levelInterrupt(Cat1·Cat2)은 Full/Non Preemptive 여부와 무관하게 항상 Task를 선점하며, 이 때 Context Switching이 발생한다. ISR 중에는 rescheduling이 일어나지 않고, Cat2 ISR 종료 후에만 Task에 대한 rescheduling이 수행된다. ISR 내부에서 activate된 Task는 모든 interrupt routine이 끝난 후에야 스케줄될 수 있다.
Nested Cat1·Cat2 — Unbounded Delay 문제
Category가 다른 인터럽트가 중첩될 때 시나리오별 동작이 다르다.
문제 시나리오 — Cat1 ISR 도중 Cat2 ISR 발생 → Cat2가 ActivateTask로 Task 깨움 → Cat1 ISR이 나중에 종료되지만 Cat1은 OS를 call하지 않으므로, activate된 Task에 대한 rescheduling이 무한 지연(unbounded delay) 될 수 있다.
해결책 — 모든 Cat1 ISR의 priority를 Cat2 ISR보다 같거나 높게 구성한다. 그러면 Cat1 ISR 도중 Cat2 ISR이 끼어들 수 없고, Cat2 ISR이 먼저 처리되어 정상적으로 OS 스케줄링이 이루어진다.
Interrupt 제어 API
중첩 허용 여부와 대상 범위가 3쌍으로 나뉜다.
| 쌍 | 대상 | Nesting |
|---|---|---|
DisableAllInterrupts / EnableAllInterrupts | 모든 interrupt | 불허 |
SuspendAllInterrupts / ResumeAllInterrupts | 모든 interrupt | 허용 |
SuspendOSInterrupts / ResumeOSInterrupts | Category 2 only | 허용 |
Nesting 허용이 중요한 이유 — 중첩 허용 쌍은 Suspend를 N회 호출하면 Resume을 동일 N회 불러야 interrupt가 재활성화된다 (depth counter). 중첩되는 critical section이 있어도 안전하게 쌓을 수 있다.
Task·ISR 간 자원 공유
Task끼리, 또는 Task와 ISR2 사이에 공유 데이터가 있으면 경쟁 상태가 발생할 수 있다. → OSEK 리소스가 임계 영역(critical section) 수단 제공.