인터럽트(Interrupt)

SungBum Park·2019년 12월 18일
0

인터럽트란?

Interrupt의 사전적 의미는 '일시 정지', '방해하다.', '가로막다.', '중단하다.' 등이 있고, 일상생활에서도 폭넒게 쓰이는 단어이다. 따라서 인터럽트는 일반적인 상황에서 갑자기 발생하는 비동기적인 이벤트 를 말한다.

그러면 소프트웨어 관점에서 인터럽트를 살펴보자. CPU가 한 프로세스에서 작업을 수행하는 도중에 인터럽트가 발생하면 하던 일을 중단하고 이미 정해진 코드를 실행해서 인터럽트의 요청을 처리하는 방식으로 동작한다. 여기서 이미 정해진 코드 흐름을 인터럽트 서비스 루틴(Interrupt Service Routine, ISR) 또는 인터럽트 핸들러(Interrupt Handler)라고 부른다.

인터럽트 종류

인터럽트를 나누는 기준은 여러가지가 있을 수 있다. 여기서는 하드웨어 인터럽트, 소프트웨어 인터럽트, 내부 인터럽트 이렇게 세 가지로 나눠서 살펴보자.

하드웨어 인터럽트

기본적으로 인터럽트라고 하면 대부분 하드웨어 인터럽트를 말한다고 볼 수 있다. 그만큼 인터럽트 중에서 가장 빈번하게 발생한다. 하드웨어 인터럽트는 주로 다음과 같다.

  • 키보드와 마우스와 같은 주변 입출력 장치에서 보내는 데이터 전송
  • 네트워크 패킷 요청

소프트웨어 인터럽트

소프트웨어 인터럽트는 사용자가 swi, int 와 같은 명령어를 통해 운영체제의 서비스를 사용하기 위해 운영체제에게 요청하는 작업을 말한다. 이를 시스템콜이라고도 부른다. 인터럽트는 각자 고유의 번호를 가지고 있는데, 시스템콜은 리눅스에서 인터럽트 0x80번(128번)으로 정의되어 있다.

내부 인터럽트

내부 인터럽트는 프로그램을 수행하는 도중에 발생하는 예외상황을 주로 처리한다.

  • 0으로 나누는 행위
  • Overflow, Underflow
  • ...

인터럽트 번호

각 인터럽트는 고유의 번호를 가지고 있다. 인터럽트 번호 자체는 예외상황 인터럽트를 제외하고는 운영체제에서 결졍한다. 리눅스에서 인터럽트 번호는 다음과 같다.

  • 0 ~ 31: 예외사항 인터럽트(20 ~ 31은 아직 정의되지 않음)
  • 32 ~ 47: 하드웨어 인터럽트(주변장치 개수에 따라 증감 가능)
  • 128: 시스템 콜

바이오스도 운영체제가 로드되기 전까지 세스템을 제어해야 하므로 인터럽트가 설정되어 있다. 바이오스 인터럽트 번호는 다음과 같다.

  • 0 ~ 7: 예외상황 인터럽트
  • 8 ~ 19: 하드웨어 인터럽트
  • 10, 13, ...: 시스템 콜

바이오스가 설정한 인터럽트 번호와 리눅스가 설정한 번호는 다르다. 리눅스는 로딩이 완료되면 제일 먼저 바이오스의 인터럽트 번호를 제거하고 리눅스 인터럽트 번호를 올린다.

인터럽트 발생

하드웨어 인터럽트 발생을 CPU에게 알리는 동작은 8259A 칩에서 수행한다. 8259A 칩은 총 8개 단자가 있으며, 한 단자에 하나의 주변장치를 담당한다. 만약 주변장치가 8개보다 많은 경우에는 8259A 칩을 두 개 사용하여, 하나는 master, 하나는 slave로 하여 slave 역할의 칩을 master 역할의 칩에 연결한다. Slave 칩은 인터럽트가 발생하면 master 칩에게 알려야 하므로, master 칩의 2번 단자에 slave 칩을 연결한다.

입력단자에 연결된 주변장치에서 인터럽트가 발생하면 8259A 칩은 해당 입력단자의 번호(irq 번호: Interrupt Request 번호)와 내부 메모리에 저장된 base 값을 더한 값을 인터럽트 번호로 설정하여 CPU에게 전기신호를 보낸다. Base 값을 설정함으로써 8259A 칩이 CPU에게 보내는 인터럽트 번호를 제어할 수 있다. 리눅스는 master 칩의 base 값은 32번으로, slave 칩의 bas 값은 40번으로 설정한다.

만약 1번 입력단자(irq 번호가 1번)에 연결된 키보드가 눌려 인터럽트가 발생하면 master 8259A 칩은 33번(base 32 + irq 1 = 33)으로 CPU에게 전달한다. 이를 리눅스의 명령어로 나타내면 INT 33 이다.

인터럽트 처리 과정

1. Command execution

여기서 PC는 다음 실행해야하는 주소를 저장하고 있다. 이 과정은 명령어가 실행되는 도중에 IDE 장치에서 인터럽트가 발생한 상황이다.

2. Check interrupt

인터럽트를 체크하는 시점은 명령어가 끝나고 난 후이다. 따라서 명령어를 수행하는 중간에 인터럽트가 발생하더라도 바로 인터럽트를 체크하지 않고, 명령어 사이클이 종료되었을 때 인터럽트 체크를 시작한다.

여기서 명령어 사이클(Instruction Cycle)은 CPU가 한 개의 명령어를 처리하는 과정으로 인출 사이클과 실행 사이클 두 개로 나눠져 있다.

  • 인출 사이클(Fetch Cycle): CPU가 기억장치로부터 명령어를 읽어오는 단계
  • 실행 사이클(Execution Cycle): 명령어를 실행하는 단계

그림에서도 명령어가 끝난 시점에 실행한 것과 같이 인터럽트 체크는 한 명령어 사이클이 모두 종료된 후에 시작한다.

3. Store return address

CPU는 인터럽트가 발생했을 때 해당 ISR로 이동하기 전에 현재 프로세스 상태를 저장한다. 이를 현제 context(컨텍스트)를 저장한다고 말하는데, 컨텍스트는 프로세스가 실행중이라는 의미이다. 프로세스가 실행중이라는 말은 CPU가 해당 프로세스를 수행하고 있다는 것이고 CPU의 레지스터 값들이 해당 프로세스의 정보들로 채워져있다는 것이다. 현재 CPU의 레지스터에 저장되어 있는 실행중이던 프로세스의 정보들을 지정된 위치에 기억해야 한다.

CPU에 저장되어 있는 주요 레지스터 값을 저장하는 위치는 스택이나 TSS(Task State Segment)가 가르키는 곳이다. 주요 레지스터로 3개만을 저장하는 경우에는 현 스택에 저장하고 5개의 레지스터를 저장하는 경우(보호모드에서 사용자모드를 수행 중 인터럽트가 발생한 경우) TSS가 가르키는 곳에 저장한다.(이 부분에 대해서는 더 공부가 필요하다.)

4. Start interrupt handling

CPU는 주요 레지스터 값을 저장한 후 인터럽트 벡터로 가서 해당 ISR 위치를 찾는다. 인터럽트 벡터는 설정된 ISR 위치를 저장해놓은 테이블이다. 인텔 x86에서는 이를 IDT(Interrupt Descriptor Table)이라고 하며, CPU는 IDTR(Interrupt Descriptor Table Register)라는 특수한 레지스터에 IDT 위치를 저장해놓는다.

5. Modify program counter

인터럽트 벡터 테이블에서 찾은 ISR 주소로 점프한다.

6. Interrupt Handler Routine for IDE device

IDE 장치 인터럽트를 실제로 처리하는 과정이다. 여기서부터는 운영체제 코드가 동작하기 시작한다. ISR은 실제 인터럽트 처리 루틴으로 점프하기 전에 준비사항을 처리하는 코드와 실제 처리루틴으로 나뉜다. 준비하는 단계를 ISR1, 실제 처리를 ISR2라고 하자.

7. Restore return address

인터럽트가 발생하기 전에 수행되던 주소로 돌아가서 다시 정상적으로 해당 프로세스를 수행한다.

출처

profile
https://parker1609.github.io/ 블로그 이전

0개의 댓글