# 인터럽트 서비스 루틴(ISR)의 설계 및 구현

인터럽트 서비스 루틴(ISR, Interrupt Service Routine)은 실시간 시스템에서 중요한 역할을 한다. ISR은 특정 이벤트가 발생했을 때 실행되며, 이러한 이벤트는 보통 하드웨어 인터럽트로 인해 발생한다. Preempt RT와 같은 실시간 시스템에서 ISR의 설계 및 구현은 시스템의 전체적인 성능과 실시간성에 직접적인 영향을 미치므로, 주의 깊게 설계되어야 한다.

#### 인터럽트 서비스 루틴의 개념

ISR은 하드웨어 장치나 다른 외부 이벤트에 의해 발생한 인터럽트 신호에 반응하여 실행되는 코드 블록이다. 일반적으로, ISR은 다음과 같은 작업을 수행한다:

* 인터럽트 발생 원인 확인
* 관련된 데이터 수집 또는 처리를 시작
* 후속 작업을 처리하기 위한 플래그 설정 또는 스레드 웨이크업
* 인터럽트 종료 및 시스템 상태 복원

ISR은 매우 빠르게 실행되어야 하며, 가능한 한 최소한의 작업만 수행해야 한다. 이는 ISR이 긴 시간 동안 실행되면 다른 중요한 실시간 작업이 지연될 수 있기 때문이다.

#### ISR의 기본 설계 원칙

1. **간결성**: ISR은 가능한 한 간결해야 한다. ISR 내에서 긴 작업을 수행하면 시스템의 실시간 성능이 저하될 수 있다.
2. **상태 보호**: ISR이 실행되는 동안 인터럽트가 비활성화될 수 있으므로, 중요한 시스템 상태가 손상되지 않도록 주의해야 한다. 필요에 따라 ISR에서는 특정 변수나 메모리 영역에 대해 원자적(atomic) 작업을 수행해야 한다.
3. **ISR과 스케줄링**: Preempt RT에서는 ISR이 실행될 때 실시간 스케줄러가 작동하여 더 높은 우선순위의 작업이 즉시 실행될 수 있다. 이 경우 ISR이 너무 오래 실행되면, 실시간 스케줄러의 효과가 저하될 수 있다.
4. **중복 방지**: 동일한 인터럽트가 중첩되어 발생하는 것을 방지하기 위해, ISR이 실행 중일 때 해당 인터럽트를 일시적으로 비활성화할 수 있다.

#### ISR의 구현 절차

ISR을 구현하는 절차는 다음과 같다:

1. **인터럽트 소스 식별 및 초기화**: ISR이 처리할 인터럽트 소스를 식별하고, 해당 인터럽트를 활성화하기 위해 하드웨어 또는 소프트웨어적으로 초기화한다.
2. **ISR 등록**: ISR은 특정 인터럽트 벡터에 등록된다. 이 과정에서는 해당 인터럽트 발생 시 실행될 함수를 시스템에 알려준다.
3. **ISR 구현**: ISR 함수는 다음과 같은 구조로 작성된다:
   * 인터럽트 원인 판별
   * 필요한 데이터 수집 또는 처리
   * 후속 작업 트리거
   * 인터럽트 플래그 클리어 및 종료

```c
void my_isr_handler(void) {
    // 인터럽트 원인 판별
    if (check_interrupt_source()) {
        // 데이터 수집 및 처리
        process_data();
        
        // 후속 작업 트리거
        trigger_next_task();
        
        // 인터럽트 플래그 클리어
        clear_interrupt_flag();
    }
}
```

#### ISR의 시간 제한 및 실시간성

ISR의 설계에서 중요한 요소 중 하나는 시간 제한이다. ISR이 너무 오래 실행되면 실시간 시스템의 응답 시간이 길어져 실시간성을 보장할 수 없게 된다.

실시간 시스템에서 ISR의 실행 시간을 제어하는 방법으로는 다음과 같은 것들이 있다:

* **ISR의 작업 최소화**: ISR 내에서 수행할 작업을 최소화하고, 더 복잡한 작업은 일반 스레드로 넘깁니다. 이를 위해 ISR에서 플래그를 설정하거나 이벤트를 발생시켜 후속 작업을 스케줄링하는 방식으로 구현한다.
* **인터럽트 우선순위 관리**: 중요한 인터럽트는 높은 우선순위로 설정하고, 덜 중요한 인터럽트는 낮은 우선순위로 설정하여 ISR의 실행 순서를 제어한다.
* **실시간 클록 활용**: ISR의 실행 시간을 실시간 클록을 이용해 측정하고, 이를 바탕으로 성능을 최적화한다.

#### 인터럽트 지연(Latency) 분석

실시간 시스템에서 인터럽트 지연(Latency)은 중요한 성능 지표이다. 인터럽트 지연은 인터럽트가 발생한 시점부터 ISR이 실제로 실행되기까지의 시간이다. 이 지연은 시스템의 다른 부하에 의해 발생할 수 있으며, 실시간 애플리케이션의 응답성을 결정짓는 중요한 요소이다.

인터럽트 지연은 다음과 같은 요소들에 의해 영향을 받을 수 있다:

1. **인터럽트 우선순위**: 시스템에서 발생하는 다양한 인터럽트는 서로 다른 우선순위를 갖는다. 우선순위가 높은 인터럽트가 낮은 우선순위의 ISR을 선점하게 되므로, 낮은 우선순위의 ISR은 더 긴 지연을 겪을 수 있다.
2. **중첩된 인터럽트**: 만약 시스템이 중첩된 인터럽트를 허용한다면, 실행 중인 ISR이 더 높은 우선순위의 인터럽트에 의해 중단될 수 있다. 이 경우 낮은 우선순위의 ISR은 재개될 때까지 지연을 겪게 된다.
3. **커널의 인터럽트 처리 시간**: 커널이 인터럽트를 처리하는 시간 자체도 지연을 발생시킬 수 있다. 예를 들어, 커널이 현재 다른 중요한 작업을 수행 중일 때 인터럽트 처리 시간이 늘어날 수 있다.

**수학적 모델링**

인터럽트 지연을 수학적으로 모델링할 수 있다. ISR의 지연 시간을 $L$이라고 하고, 다음과 같은 변수들을 고려해 볼 수 있다:

* $T\_{arrival}$: 인터럽트가 발생한 시점
* $T\_{start}$: ISR이 실제로 실행을 시작한 시점
* $T\_{delay}$: ISR이 실행되기까지의 지연 시간

이를 통해 인터럽트 지연은 다음과 같이 정의할 수 있다:

$$
L = T\_{start} - T\_{arrival}
$$

지연을 최소화하기 위해서는 $T\_{delay}$를 줄이는 것이 중요하다. 이를 위해서는 커널 수준에서의 최적화와 ISR 설계 최적화가 필요하다.

#### 데이터 공유 및 동기화

ISR과 다른 스레드 간의 데이터 교환은 매우 주의 깊게 이루어져야 한다. 실시간 시스템에서 이러한 데이터 교환이 잘못되면 시스템이 불안정해질 수 있다. 일반적으로 ISR은 데이터를 전역 변수 또는 메모리 버퍼에 저장하고, 이를 실시간 스레드에서 읽습니다. 그러나 이 과정에서 동기화 문제가 발생할 수 있다.

**데이터 경합 방지**

ISR이 공유 자원에 접근하는 동안 다른 스레드가 같은 자원에 접근하려고 할 때 데이터 경합이 발생할 수 있다. 이를 방지하기 위해 다음과 같은 방법을 사용할 수 있다:

* **디스에이블 인터럽트**: 데이터를 수정하기 전 ISR은 인터럽트를 비활성화할 수 있다. 이 방법은 간단하지만, 전체 시스템의 응답성을 저하시킬 수 있다.
* **스핀락(Spinlock)**: 다중 프로세서 시스템에서 사용하는 방법으로, 하나의 프로세서가 자원을 사용하는 동안 다른 프로세서는 대기한다.
* **메모리 배리어(Memory Barrier)**: 메모리 배리어를 사용하여 프로세서가 명령어의 순서를 변경하지 않도록 보장할 수 있다. 이를 통해 메모리 접근의 순서를 제어할 수 있다.

**예제 코드**

다음은 ISR과 실시간 스레드 간에 데이터를 안전하게 교환하는 간단한 예제이다:

```c
volatile int shared_data = 0;
volatile bool data_ready = false;

void my_isr_handler(void) {
    shared_data = read_hardware_data();
    data_ready = true;  // 데이터가 준비되었음을 알림
}

void realtime_thread(void) {
    while (1) {
        if (data_ready) {
            process_data(shared_data);
            data_ready = false;  // 데이터 처리가 완료되었음을 알림
        }
        sleep_until_next_period();
    }
}
```

이 예제에서 `shared_data`와 `data_ready` 변수는 `volatile` 키워드를 사용하여 컴파일러 최적화를 방지한다. 이로 인해 ISR과 실시간 스레드 간의 데이터 교환이 올바르게 이루어질 수 있다.

#### 최적의 ISR 설계 전략

1. **빠른 실행**: ISR 내에서 가능한 한 적은 양의 코드를 실행하고, 복잡한 작업은 실시간 스레드로 넘깁니다.
2. **효율적인 데이터 처리**: ISR에서 중요한 데이터를 빠르게 처리하고, 나머지 작업은 후속 작업으로 처리한다.
3. **선점 방지**: ISR의 실행 중에는 가능한 한 다른 인터럽트가 발생하지 않도록 설계한다.
4. **타이밍 제어**: ISR의 실행 시간을 측정하고, 필요에 따라 최적화한다.
5. **하드웨어 지원 활용**: 현대의 많은 하드웨어는 ISR 실행을 지원하는 기능을 제공한다. 이러한 기능을 활용하여 성능을 최적화할 수 있다.
