정의
AUTOSAR OS는 OSEK/VDX 운영체제를 핵심 기능으로 삼고 그 위에 스케줄 테이블과 타이밍 보호·메모리 보호를 얹은 Classic 플랫폼의 운영체제 규격이며, OSEK OS용으로 작성된 응용이 그대로 돌도록 API 하위 호환을 요구한다.[1]
OSEK/VDX 운영체제는 이미 업계에 널리 쓰였고 모든 등급의 ECU에서 검증된 상태였다. 이벤트 트리거 운영체제라서 무엇이 스케줄링을 구동할지를 자유롭게 고를 수 있다는 점도 컸다 — 축의 회전이든 로컬 시간원이든 전역 시간원이든 오류 발생이든 상관없다. ISO 17356-3[2]이 정한 이 운영체제가 AUTOSAR에 건네준 것은 고정 우선순위 스케줄링, 인터럽트 처리 수단, 태스크보다 높은 인터럽트 우선순위, OS 서비스 오용에 대한 일정한 보호, 그리고 기동과 종료의 인터페이스다.[1] 확장된 기능 가운데 일부는 기존 OSEK 기능의 사용에 제약을 걸거나 그것을 늘려 잡는다.
태스크와 인터럽트 처리, 카운터와 알람 같은 객체 자체는 OSEK/VDX 규격의 몫이다. 이 페이지는 AUTOSAR가 그 위에 더한 것을 다룬다.
확장성 등급
보호 기능은 하드웨어가 받쳐 줘야 쓸 수 있는 것이 있고, 메모리 보호 유닛이 없는 마이크로컨트롤러가 그래서 나머지 보호 기능까지 못 쓰게 되어서는 곤란하다. 보호가 필요한 범위도 응용마다 다르다.[3] 그래서 규격은 기능 묶음을 네 확장성 등급(scalability class)으로 나눠, 프로세서가 가진 기능을 살리면서 사용자에게 필요한 만큼만 고르게 한다.[1]
| 기능 | SC1 | SC2 | SC3 | SC4 | 하드웨어 요구 |
|---|---|---|---|---|---|
| OSEK OS 전 적합성 클래스, 카운터 인터페이스, 스케줄 테이블, 스택 감시 | 있음 | 있음 | 있음 | 있음 | 없음 |
| 보호 훅 | 없음 | 있음 | 있음 | 있음 | 없음 |
| 타이밍 보호 | 없음 | 있음 | 없음 | 있음 | 높은 우선순위 인터럽트를 내는 타이머 |
| 전역 시간 동기화 지원 | 없음 | 있음 | 없음 | 있음 | 전역 시간원 |
| 메모리 보호, 서비스 보호, 신뢰 함수 호출 | 없음 | 없음 | 있음 | 있음 | 메모리 보호 유닛, 특권·비특권 모드 |
OS 애플리케이션은 SC3과 SC4에서만 필수이고, 나머지 등급에서는 정의하지 않아도 된다. 등급마다 최소 수량도 못박혀 있다 — 스케줄 테이블은 SC1과 SC3에서 2개, SC2와 SC4에서 8개가 최소이고, 소프트웨어 카운터는 어느 등급에서나 8개가 최소다. SC3과 SC4에서는 운영체제가 항상 확장 상태(extended status)를 쓴다.
등급 번호의 뜻
스케줄 테이블
정적으로 정해진 태스크 활성화는 카운터 하나와 자동 시작되는 알람 여러 개로도 만들 수 있다. 다만 그 방식은 알람을 한 번 걸어 둔 뒤 건드리지 않을 때만 단순하다. 실행 중에 손을 대려면 알람들 사이의 상대적 동기가 보장되어야 하고, 보통은 해당 카운터의 틱 인터럽트를 막아 둔 채 고쳐야 한다. 스케줄 테이블은 정적으로 정의된 만료 지점(expiry point)의 집합을 하나로 캡슐화해 이 동기화 문제를 없앤다.[1] 테이블 기반 스케줄이 알람 서비스로 활성화하는 태스크보다 효율적이고 이해하기 쉽다는 것이 요구사항 문서가 적은 근거다.[3]
만료 지점 하나는 그 지점에서 벌어질 동작과, 테이블 시작으로부터의 틱 오프셋을 담는다. 동작은 태스크 활성화, 이벤트 설정, 그리고 deferrable server로 구성된 태스크의 실행 예산 재충전 세 가지이며, 빈 만료 지점은 쓸 자리가 없으므로 지점마다 적어도 하나는 있어야 한다. 같은 테이블 안의 만료 지점은 오프셋이 서로 달라야 하는데, 그래야 처리 순서가 하나로 정해지기 때문이다. 한 지점에서 어떤 태스크의 활성화와 그 태스크의 이벤트 설정이 함께 일어날 때는 활성화가 먼저다 — 순서가 뒤집히면 구현마다 결과가 달라진다.
테이블을 흘리는 것은 카운터다. 테이블 하나는 정확히 하나의 카운터가 구동하고, 카운터의 틱 하나가 테이블의 틱 하나에 대응한다. 그래서 카운터의 성질이 테이블에 걸 수 있는 오프셋의 범위를 제한한다. 테이블에는 길이가 있고, 그 길이가 테이블의 modulus가 된다. 테이블 시작에서 첫 만료 지점까지의 거리를 최초 오프셋, 마지막 만료 지점에서 테이블 끝까지의 거리를 종료 지연이라 부른다.
동작 방식은 두 가지로 구성할 수 있다. 한 번만 도는 방식은 마지막 만료 지점을 처리하고 종료 지연만큼 지난 뒤 멈추며, 어떤 계기에 반응해 단계적인 동작 열을 트리거할 때 쓴다. 반복 방식은 마지막 지점을 처리한 뒤 종료 지연과 최초 오프셋을 더한 시간이 지나면 다시 첫 지점으로 돌아간다. 결과적으로 각 만료 지점은 테이블 길이를 주기로 되풀이된다. 여러 테이블을 동시에 처리하는 것도 허용된다.
활성화 시점이 구성 시점에 고정된다는 점에서 이 메커니즘은 순환 스케줄링의 디스패처 테이블과, 실행 시각을 설계 시점에 못박는 시간 구동 아키텍처와 같은 계열에 놓인다.
타이밍 보호
타이밍 결함은 태스크나 인터럽트가 데드라인을 놓칠 때 일어난다. 그런데 AUTOSAR OS는 데드라인 감시를 타이밍 보호 수단으로 쓰지 않는다. 데드라인을 놓친 태스크가 결함을 일으킨 태스크라는 보장이 없기 때문이다 — 무관한 태스크가 규정보다 오래 실행하거나 너무 오래 자원을 잡고 있으면 그 여파가 아래로 전파되어, 정상 동작하던 태스크가 대신 데드라인을 놓친다. 이때 데드라인 위반을 근거로 조치하면 정상인 쪽을 종료시키고 비정상인 쪽을 계속 돌리게 된다.[1]
그래서 규격은 결과가 아니라 원인을 통제한다. 고정 우선순위 선점 운영체제에서 데드라인 준수를 좌우하는 것은 태스크와 ISR의 실행 시간, 낮은 우선순위 쪽이 락을 잡거나 인터럽트를 막아 생기는 블로킹 시간, 그리고 도착 간격 셋이다. 타이밍 보호는 이 셋에 각각 정적 경계를 건다.
- 실행 예산(execution budget) — 태스크와 category 2 ISR의 실행 시간 상한
- 락 예산(lock budget) — 자원을 잡고 있는 시간, OS 인터럽트를 막아 두는 시간, 모든 인터럽트를 막아 두는 시간의 상한
- 시간 프레임(time frame) — 태스크가 준비 상태로 옮겨 오는 간격, category 2 ISR이 도착하는 간격의 하한
앞의 둘이 상한이고 마지막이 하한인 이유는 통제하려는 대상이 다르기 때문이다. 도착 간격에 하한을 강제하면 인터럽트를 쉼 없이 쏟아 내는 고장 노드로부터 ECU를 지킬 수 있다. 실행 시간의 상한을 어떻게 잡을지는 최악 실행 시간의 문제다.
보호 밖에 있는 것
타이밍 보호는 태스크와 category 2 ISR에만 적용된다. 운영체제가 존재를 알지 못하는 category 1 ISR은 보호 대상이 아니며, 그 안에서 타이밍 보호 오류가 나면 운영체제의 일관성을 보장할 수 없다. 운영체제가 시작되기 전에도 보호는 작동하지 않는다.[1]
OS 애플리케이션
태스크·ISR·알람·스케줄 테이블·카운터를 하나의 기능 단위로 묶은 것이 OS 애플리케이션(OS-Application)이다. OS 애플리케이션을 쓰면 모든 객체가 어느 한 애플리케이션에 속해야 하고, 같은 애플리케이션에 속한 객체끼리는 서로 접근할 수 있으며 다른 애플리케이션의 객체에 대한 접근 권한은 구성 시점에 부여된다. 자원 객체는 어느 애플리케이션에도 속하지 않지만 접근은 명시적으로 허가받아야 한다.[1]
이 묶음이 필요한 이유는 고장 격리다. OSEK OS에서는 객체의 소유 관계가 느슨해서, 예컨대 한 소프트웨어 컴포넌트가 다른 컴포넌트의 알람을 잘못 취소하는 종류의 결함을 실행 중에 가둘 수 없다.[3]
애플리케이션은 두 부류로 갈린다. 신뢰(trusted) 애플리케이션은 감시·보호 기능을 끈 채 돌 수 있고 메모리와 OS API에 제한 없이 접근하며 프로세서가 지원하면 특권 모드로 실행된다. 비신뢰(non-trusted) 애플리케이션은 그럴 수 없다 — 메모리와 API 접근이 제한되고 타이밍 거동이 실행 중에 강제되며 특권 모드로 실행되지 않는다. 운영체제 자신은 신뢰 대상으로 가정된다. 각 애플리케이션은 기동 시 접근 가능 상태로 시작해, 종료되면 다른 애플리케이션이 그 객체에 접근할 수 없는 상태로 넘어간다.
메모리 보호
메모리 보호는 하드웨어가 받쳐 주는 프로세서에서만 가능하다. 보호의 단위는 가상 주소 공간이 아니라 실행 프로그램의 데이터·코드·스택 섹션이다.[1]
스택은 태스크나 ISR 하나에만 속하므로 같은 OS 애플리케이션 안에서도 공유할 이유가 없다. 스택을 보호해 두면 두 가지를 얻는다 — 스택 감시보다 빠르게 오버플로와 언더플로를 잡아내고, 한 애플리케이션 안에서도 구성 요소 사이에 경계를 세워 안전 요구를 만족시킬 수 있다. 데이터 섹션은 OS 애플리케이션 단위의 것과 태스크·ISR 단위의 것으로 나뉘며, 애플리케이션의 사적 데이터는 그 안의 태스크와 ISR이 공유한다. 코드 섹션은 애플리케이션에 사적이거나 공유 라이브러리처럼 모두에게 열려 있거나 둘 중 하나다.
강제의 방향은 쓰기 접근이 기준이다. 운영체제는 자기 데이터 섹션과 스택에 대한 비신뢰 애플리케이션의 쓰기를 막고, 어떤 OS 애플리케이션의 사적 데이터 섹션에 대해서도 다른 비신뢰 애플리케이션의 쓰기를 막는다. 읽기 접근을 막는 것은 선택 사항이다.