요약

인터럽트는 “다 됐어요” 하고 어깨를 두드리는 신호다. 요리사가 오븐 앞에 서서 빵이 다 구워지기를 지켜보는 대신, 다른 요리를 하다가 타이머가 울리면 그때 오븐으로 간다. 덕분에 느린 장치를 기다리는 동안 프로세서가 놀지 않는다. 대신 언제 끊길지 모르므로, 끊긴 프로그램의 상태를 빠짐없이 저장했다가 그대로 되살려야 한다.

예시로 보기

프로그램이 프린터로 글을 보낸다고 하자. 프린터는 프로세서보다 훨씬 느리다. 한 번 쓰고 나면 프린터가 따라올 때까지 명령어 사이클 수천~수백만 번 동안 기다려야 한다.

  인터럽트가 없으면 인터럽트가 있으면
WRITE 호출 뒤 입출력 프로그램이 프린터가 끝날 때까지 붙잡고 기다린다. 사용자 프로그램은 그동안 멈춘다 입출력 프로그램은 준비와 명령 내리기만 하고 곧바로 돌아온다
프린터가 일하는 동안 프로세서는 아무것도 못 한다 사용자 프로그램이 계속 실행된다
프린터가 끝나면 기다리던 입출력 프로그램이 마무리한다 프린터 쪽 입출력 모듈이 인터럽트 신호를 보낸다. 프로세서는 하던 일을 잠깐 멈추고 처리 루틴을 실행한 뒤 돌아온다

인터럽트는 사용자 프로그램의 아무 명령어 사이에서나 생길 수 있다. 그래도 사용자 프로그램에는 인터럽트를 위한 코드가 따로 필요 없다. 멈추고 다시 시작하는 일은 프로세서와 운영체제가 맡는다.

입출력이 사용자 프로그램의 다음 WRITE보다 늦게 끝나는 경우가 더 흔하다. 그러면 사용자 프로그램은 두 번째 WRITE에서 다시 기다린다. 그래도 입출력과 계산이 겹치는 구간만큼은 이득이다.

정확히 말하면

정의

인터럽트는 입출력 모듈, 메모리 같은 다른 부분이 프로세서의 평소 실행 순서를 끊는 장치다. 끊으면 프로세서는 지금 명령어를 마친 뒤, 지금 프로그램을 멈추고 인터럽트 처리기(interrupt handler)를 실행한다. 처리기가 끝나면 멈춘 자리부터 다시 실행한다.

인터럽트가 생기는 원인은 네 가지다.

종류 언제 생기나
프로그램 명령어를 실행한 결과로. 산술 넘침, 0으로 나누기, 잘못된 기계어 실행, 허락되지 않은 메모리 접근
타이머 프로세서 안 타이머가 울릴 때. 운영체제가 정해진 간격마다 일을 할 수 있게 한다
입출력 입출력 장치가 일을 끝냈거나 오류가 났을 때
하드웨어 고장 정전, 메모리 패리티 오류 같은 고장

명령어 사이클에는 인터럽트 단계가 하나 더 붙는다. 실행 단계가 끝날 때마다 인터럽트 신호가 와 있는지 확인한다.

flowchart LR
  F[인출] --> E[실행]
  E --> Q{인터럽트를<br/>받는 중이고<br/>신호가 왔나?}
  Q -- 아니오 --> F
  Q -- 예 --> I[인터럽트 단계<br/>상태 저장 후<br/>처리기로 이동]
  I --> F

처리 순서

입출력 장치가 일을 마치면 다음 순서로 진행한다. 1~6은 하드웨어가, 7~9는 소프트웨어(처리기)가 한다.

  1. 장치가 프로세서에 인터럽트 신호를 보낸다.
  2. 프로세서는 지금 실행 중인 명령어를 끝까지 마친다.
  3. 프로세서가 대기 중인 인터럽트를 확인하고, 보낸 장치에 “받았다”는 신호를 보낸다. 장치는 그제야 신호를 내린다.
  4. 처리 루틴으로 옮겨 갈 준비를 한다.
  5. PC에 처리 루틴의 시작 주소를 넣는다.
  6. 이 시점까지 끊긴 프로그램의 PC와 PSW가 제어 스택에 저장되어 있다.
  7. 처리기가 인터럽트를 처리한다. 먼저 나머지 레지스터를 저장한다.
  8. 처리가 끝나면 저장해 둔 레지스터 값을 스택에서 꺼내 되돌린다.
  9. 마지막으로 PSW와 PC를 되돌린다. 끊긴 프로그램이 다음 명령어부터 이어서 실행된다.
sequenceDiagram
  participant D as 입출력 장치
  participant P as 프로세서 하드웨어
  participant S as 제어 스택
  participant H as 인터럽트 처리기
  D->>P: 인터럽트 신호
  Note over P: 지금 명령어를 끝까지 마친다
  P->>D: 받았다는 신호
  P->>S: 끊긴 프로그램의 PC와 PSW 저장
  P->>H: PC에 처리 루틴 시작 주소를 넣는다
  H->>S: 나머지 레지스터 저장
  Note over H: 인터럽트 처리
  S->>H: 나머지 레지스터 복원
  S->>P: PSW와 PC 복원
  Note over P: 끊긴 프로그램의 다음 명령어부터 실행

위에서 아래로 따라가면 1~6단계는 하드웨어가, 7~9단계는 처리기가 한다. 스택에는 PC·PSW가 먼저 들어가고 나머지 레지스터가 나중에 들어간다. 꺼낼 때는 그 반대 순서다.

예를 들어 N번지 명령어를 마친 직후에 끊기면, 레지스터 값 전부와 다음 명령어 주소 N + 1(합쳐서 M워드)을 제어 스택에 넣는다. 스택 포인터는 새 맨 위를 가리키고, PC는 처리 루틴의 시작을 가리킨다.

왜 상태를 전부 저장해야 하나

함수 호출은 프로그램이 스스로 부르는 것이라서, 부르기 전에 무엇을 저장할지 프로그램이 정할 수 있다. 인터럽트는 프로그램이 부르지 않았고, 언제 올지도 모른다. 그래서 처리기가 쓸지도 모르는 레지스터를 모두 저장해야, 끊긴 프로그램은 끊긴 줄도 모르고 이어서 실행된다. 하나라도 빠뜨리면 처리기가 덮어쓴 값을 끊긴 프로그램이 그대로 이어받아 계산이 틀어진다.

인터럽트를 처리하는 중에 또 인터럽트가 오면

예를 들어 통신선으로 데이터를 받으면서 프린터로 출력하는 프로그램은 두 장치에서 번갈아 인터럽트를 받는다. 두 가지 방법이 있다.

  순차 처리 중첩 처리
방법 처리하는 동안 인터럽트를 막는다(disable). 새 신호는 무시되지 않고 대기하다가, 다시 받기 시작하면 처리된다 인터럽트마다 우선순위를 둔다. 더 높은 우선순위의 인터럽트만 지금 처리기를 끊을 수 있다
좋은 점 단순하다. 순서대로 하나씩 처리한다 급한 장치(데이터가 금방 덮어써지는 통신선 등)를 먼저 처리할 수 있다
나쁜 점 급한 요청도 앞 처리가 끝날 때까지 기다린다 저장·복원이 여러 겹으로 쌓여 복잡하다

중첩 처리의 예: 프린터(우선순위 2), 디스크(4), 통신선(5)이 있다.

시각 일어난 일 실행 중
0 사용자 프로그램 시작 사용자
10 프린터 인터럽트. 사용자 상태를 스택에 넣는다 프린터 처리
15 통신 인터럽트(5 > 2). 프린터 처리를 끊고 스택에 넣는다 통신 처리
20 디스크 인터럽트(4 < 5). 끊지 못하고 대기한다 통신 처리
25 통신 처리 끝. 프린터 처리로 돌아가려는 순간 대기 중인 디스크(4 > 2)가 먼저 들어온다 디스크 처리
35 디스크 처리 끝. 프린터 처리로 돌아간다 프린터 처리
40 프린터 처리 끝. 사용자 프로그램으로 돌아간다 사용자
검증: 위 중첩 표와, 같은 요청을 순차 처리했을 때의 표(프린터 10~20, 통신 20~30, 디스크 30~40)를 시뮬레이션으로 재현 — 04_interrupt_verify.py

보장하는 것과 보장하지 않는 것

보장한다 보장하지 않는다
끊긴 프로그램은 저장된 PC와 레지스터로 정확히 같은 자리, 같은 상태에서 이어진다 끊긴 프로그램으로 바로 돌아간다는 것. 여러 프로그램이 있으면 처리기가 끝난 뒤 다른 프로그램이 실행될 수 있다
명령어 하나는 중간에 끊기지 않는다. 지금 명령어를 마친 뒤에 확인한다 인터럽트가 즉시 처리된다는 것. 막혀 있거나 더 높은 우선순위가 처리 중이면 기다린다

스스로 설명해 보기

  1. 처리 순서 2단계: 프로세서는 지금 명령어를 끝까지 마친 뒤 인터럽트를 받는다.
    근거

    인터럽트 확인은 명령어 사이클의 실행 단계 뒤에 붙은 인터럽트 단계에서 한다. 명령어 중간에는 확인하는 자리가 없다.

  2. 처리 순서 6단계: PC와 PSW를 저장한다.
    근거

    PC가 없으면 어디서 이어야 할지 모른다. PSW가 없으면 조건 코드와 모드 비트가 처리기 것으로 바뀐 채 돌아간다. 둘 다 처리기로 넘어가는 순간 바뀌므로, 바뀌기 전에 하드웨어가 저장한다.

  3. 시각 25에서 디스크가 프린터보다 먼저 실행된다.
    근거

    통신 처리기가 끝나 스택에서 프린터 처리기를 꺼내려는 순간, 다시 인터럽트 단계에서 대기 중인 요청을 확인한다. 디스크(4)가 프린터(2)보다 높으므로 프린터 처리기는 명령어 하나도 실행하지 못한 채 다시 끊긴다.

    • 인터럽트의 핵심 아이디어는?
답

기다리는 쪽(프로세서)이 계속 확인하는 대신, 끝난 쪽(장치)이 알려 준다. 알리는 순간에 끼어들 수 있도록 상태를 저장하고 복원하는 장치를 하드웨어에 넣는다.

  • 같은 생각을 쓰는 다른 곳은?
    답

    운영체제가 타이머 인터럽트로 정해진 시간마다 프로세서를 되찾는 시분할. 프로그래밍에서는 이벤트가 오면 불리는 콜백 함수가 같은 생각이다.

활용

  • 운영체제가 프로세서를 되찾는 통로다. 사용자 프로그램이 프로세서를 쥐고 있어도 타이머 인터럽트가 오면 운영체제 코드가 실행된다. 시분할이 이것으로 돌아간다.
  • 입출력 기법 가운데 인터럽트 구동 입출력과 DMA가 이것을 쓴다.
  • 인터럽트로 기다림을 없애도, 프로그램이 하나뿐이면 결국 그 프로그램이 입출력을 기다리며 멈춘다. 그래서 프로그램 여러 개를 올려 두고 번갈아 실행하는 다중 프로그래밍이 필요해진다.

연결

자주 하는 오해

“인터럽트가 오면 프로세서는 하던 명령어를 그 자리에서 버리고 처리기로 간다”

틀렸다. 갑자기 끊는다는 이름 때문에 명령어 한가운데서 끊기는 것처럼 들린다. 실제로는 지금 명령어를 끝까지 마친 뒤 인터럽트 단계에서 확인한다(처리 순서 2단계). 확인하는 방법: 명령어 사이클 그림에서 인터럽트 확인은 실행 단계 뒤에만 있다.

“인터럽트 처리가 끝나면 항상 끊긴 프로그램으로 돌아간다”

틀렸다. 프로그램이 하나뿐인 그림만 보면 그렇게 보인다. 여러 프로그램이 메모리에 있으면, 처리기가 끝난 뒤 우선순위나 입출력 대기 여부에 따라 다른 프로그램이 실행될 수 있다. 끊긴 프로그램의 상태는 저장되어 있으므로 나중에 차례가 오면 그대로 이어진다.

과목별 관점

어셈블리어 (2-1학기). 위의 일반 원리가 16비트 x86 PC(MS-DOS)에서는 이렇게 생겼다.

  • 처리 루틴의 주소를 찾는 표. 처리 순서 5단계의 “처리 루틴의 시작 주소”는 메모리 맨 앞의 인터럽트 벡터 표에서 나온다. 인터럽트마다 번호가 있고, 번호에 4를 곱한 주소에 그 처리 루틴의 주소 4바이트가 적혀 있다: INT 명령과 인터럽트 벡터 표.
  • 프로그램이 스스로 거는 인터럽트. INT 21h처럼 명령으로 같은 표를 거쳐 처리 루틴을 부를 수 있다. MS-DOS와 BIOS에 일을 시키는 창구가 이것이다.
  • 우선순위를 정하는 칩. 장치의 신호는 8259라는 칩이 먼저 받아, 우선순위가 낮은 요청이 높은 요청의 처리를 끊지 못하게 막는다. 장치마다 정해진 선 번호가 IRQ다. 타이머가 IRQ 0, 키보드가 IRQ 1이다: 하드웨어 인터럽트와 IRQ.
  • 막고 여는 명령. 위 표의 “인터럽트를 막는다”는 CLI 명령, 다시 받는 것은 STI 명령이다. 처리 루틴은 IRET 명령으로 끝난다.

확인 문제

C1 인터럽트의 네 가지 종류와 각각의 예를 하나씩 쓰라.

답: 프로그램(0으로 나누기, 허락되지 않은 메모리 접근), 타이머(정해진 간격마다 울림), 입출력(장치 작업 완료나 오류), 하드웨어 고장(정전, 메모리 패리티 오류).

C2 함수 호출과 달리 인터럽트는 레지스터를 전부 저장해야 하는 이유는?

답: 함수 호출은 프로그램이 정한 자리에서 일어나므로 무엇을 저장할지 미리 정할 수 있다. 인터럽트는 아무 명령어 사이에서나, 프로그램이 모르게 일어난다. 처리기가 어떤 레지스터를 쓸지 모르니 모두 저장해야 끊긴 프로그램이 같은 상태로 이어진다.
흔한 오답: “인터럽트가 더 중요해서”. 중요도가 아니라 예측할 수 없다는 점이 이유다.

C3 프린터 처리기가 실행 중일 때 통신 인터럽트가 왔다. 순차 처리와 중첩 처리에서 통신 처리는 각각 언제 시작되는가? 위 중첩 예의 시각을 쓰라.

답: 중첩 처리: 통신(5)이 프린터(2)보다 높으므로 도착한 시각 15에 바로 시작한다. 순차 처리: 인터럽트가 막혀 있으므로 프린터 처리가 끝나는 시각 20까지 기다린다.

C4 위 중첩 예에서 시각 22에 제어 스택에는 무엇이 쌓여 있는가? (아래부터)

답: 맨 아래 사용자 프로그램의 상태, 그 위에 프린터 처리기의 상태. 실행 중인 것은 통신 처리기다. 디스크 요청은 스택이 아니라 대기 상태로 있다.
흔한 오답: 디스크 처리기를 스택에 넣는 것. 디스크는 아직 시작하지도 않았으므로 저장할 상태가 없다.

C5 중첩 처리, 사용자 프로그램이 t=0에 시작. 디스크(우선순위 4) t=5 도착 처리 8, 프린터(2) t=7 도착 처리 4, 통신(5) t=9 도착 처리 3. 사용자 프로그램으로 돌아오는 시각은? 구간별로 누가 실행되는지 쓰라.

답: 0–5 사용자, 5–9 디스크, 9–12 통신, 12–16 디스크, 16–20 프린터, 20부터 사용자. 돌아오는 시각은 20이다.
이유: t=7의 프린터(2)는 디스크(4)를 끊지 못해 대기한다. t=9의 통신(5)은 디스크를 끊는다. 디스크는 남은 4를 12–16에 마친다.

C6 16비트 x86 PC에서 키보드 인터럽트의 번호는 9다. 프로세서는 처리 루틴의 시작 주소를 메모리 몇 번지에서 읽는가? 처리하는 동안 다른 장치의 인터럽트를 막는 명령과 다시 받는 명령은?

답: 0024h번지에서 4바이트를 읽는다. 막는 명령은 CLI, 다시 받는 명령은 STI다.
이유: 인터럽트 벡터 표에서 칸의 주소는 번호 × 4다. 9 × 4 = 36 = 24h. 계산은 50_interrupt-vector-table_verify.py로 확인했다.