#7 운영체제 | 인터럽트는 무엇이고 왜 필요한가

HYUN·2021년 2월 20일
1

OS | 운영체제

목록 보기
7/13
post-thumbnail

인터럽트 | Interrupt

인터럽트란 CPU가 특정 기능을 수행하는 도중에 급하게 다른 일을 처리하고자 할 때 사용할 수 있는 기능이다.

대부분의 컴퓨터는 한 개의 CPU를 사용하므로 한 순간에는 하나의 일 밖에 처리할 수 없기 때문에 어떤 일을 처리하는 도중에 우선 순위가 급한 일을 처리할 필요가 있을 때 대처할 수 있는 방안이 필요하다.

예를 들면, 키보드의 키를 하나 누르면, 눌려진 키 코드 값이 키보드 버퍼에 입력된 후 CPU에 인터럽트가 걸린다. 그럼 현재 처리하던 작업에 대한 정보를 수집하여 저장한 뒤에 인터럽트 서비스 루틴(Interrupt Service Routine)을 수행한다.(이 경우에는 키보드 버퍼에 있는 키 코드 값을 가져가는 일을 한다.) 이렇게 인터럽트 처리를 마친 후에는 다시 이전에 처리하던 작업으로 돌아간다. | 출처: 나무위키

위의 설명대로 언터럽트는 CPU가 프로그램을 실행하고 있을 때, 입/출력 장치나 혹은 다른 예외상황이 발생하여 처리가 필요할 경우에 CPU에 알려서 처리하는 기술입니다. 쉽게 말해 어느 한 순간에 CPU가 실행하는 명령은 하나인데 다른 장치와의 대화는 어떻게 이루어지는가?에 대한 답으로 말할 수 있습니다.


코드로 보자면

#include <unistd.h>
#include <sys/types.h>  
#include <sys/stat.h>   
#include <fcntl.h>     

int main() {
  int fd;
  fd = open("data.txt".O_RDONLY); // 해당 코드
  if(fd == -1) {
    printf("Error: can not open fileWn");
    return 1;
  } else {
    printf("File opened and now close_Wn");
    close(td);
    return ();
  }
}

코드를 간단하게 보자면 주석처리가 되어 있는 부분의 코드에서는 변수가 선언이 끝나고 파일을 읽는 단계인걸 알 수 있습니다. 그렇다면 코드는 다음(조건문)으로 넘어가지 못합니다. 해당 프로세스가 Block 처리(상태)가 되기 때문인데요.

여기서 CPU의 입장에서보면 저장매체에서 파일을 다 읽었다면? 스케줄러에게 파일읽기를 완료했다고 알려줘야하지 않을까요? 그래야 Block 상태에서 Ready상태로 변경하고 최종적으로는 Running상태까지 갈 수 있을테니까요. 그것을 알려주는것이 이번 주제인 인터럽트라는 겁니다.


인터럽트는 왜 필요한가?

선점형 스케줄러를 예로 들면 프로세스가 Running 중에 스케줄러에 의해 중단되게 됩니다. 이유는 다른 프로세스로 교체하기 위함이죠. 그렇게 하기 위해서는 스케줄러의 코드가 실행이되서 현재 진행중인 프로세스를 중지시킬 수 있어야 합니다. 스케줄러도 하나의 프로그램이니까요.

프로세스가 스스로 결정하는것은 진행 중에 I/O장치 혹은 다른 작업을 진행해야 해서 Block 상태가 되는것과 프로세스가 종료되서 Exit상태가 되는것이지 Running 상태에서는 스케줄러에의해 강제로 Ready상태가 되는겁니다. 프로세스가 스스로 중단하는것이 아니라 스케줄러가 강제로 중단을 시키는것이고 인터럽트는 이러한 부분에서도 필요한 기능입니다.


언터럽트의 동작/처리

인터럽트의 동작 혹은 처리를 간단하게 정리하자면 CPU가 프로그램을 실행하고 있을 때 하드웨어 등의 장치 이슈(ex 파일 처리 완료)가 발생한다면 그것을 운영체제에게 알려주고 운영체제는 해당 프로세스를 Block state에서 실행 대기(Ready state) 상태로 프로세스 상태를 변경합니다.

혹은 예외 상황이 발생할때의 동작/처리에서 코드 계산에 오류가 있을 경우 이러한 예외 발생을 운영체제에게 알려주고 전달받은 운영체제는 해당 프로세스를 중지하고 에러를 표시합니다.


주요 인터럽트 종류

이러한 인터럽트는 다양한 종류가 있습니다. 일반적으로 사용되는 인터럽트는 두가지 갈래로 나뉠 수 있는데 간단하게 몇가지만 살펴보겠습니다.

종류

  • 외부 인터럽트: 입출력 장치, 타이밍 장치, 전원 등의 외부적인 요인에 의해서 발생하는 인터럽트.

    • 전원 이상 인터럽트: 정전이나 전원이 이상이 있는 경우
    • 기계 고장 인터럽트: CPU등의 기능적인 동작 오류가 발생한 경우
    • 입출력 인터럽트(I/O Interrupt): 입출력의 종료 등의 이유로 CPU의 수행을 요청하는 인터럽트.
  • 내부 인터럽트: 잘못된 명령이나 데이터를 사용할 때 발생하는 인터럽트

    • 0으로 나누는 경우
    • 오버플로우 또는 언더플로우가 발생한 경우
    • 프로그램 상의 오류
    • 프로그램에서 함수 등 명령어를 잘못 사용한 경우
    • 소프트웨어 인터럽트: CPU가 인스트럭션을 수행하는 도중에 일어나는 인터럽트

내부 인터럽트 === 소프트웨어 인터럽트
외부 인터럽트 === 하드웨어 인터럽트

1. 0으로 나누는 경우

Divide - dy - Zero Interrupt

#include <stdio.h>

int main() {
    printf("Hello World!\n");
    int data;
    int divider = 0;
    data = 1 / divider; // 해당 부분에서 인터럽트가 발생합니다.
    return 0;
}

위의 코드를 실행시켰을 때 결과를 먼저 보겠습니다.

Hello World!
Floating point exception (core dumped) // 이 오류는 나누기를 할 때 변수/0이 있으면 발생

위와 같이 간단한 코드는 컴파일 하는 과정에서는 에러가 나지 않지만 실행시켰을시에 결과는 Hello Wordl!가 나오고 위와 같은 오류를 표시합니다. 바로 운영체제에서 보여주는 오류인데 주석의 내용과 같이 변수/0을 계산할 수 없기 때문에 변수/0의 인터럽트가 발생하고 운영체제는 사용자에게 인터럽트에게 받은 정보를 보여주는겁니다.

2. 타이머 인터럽트 | 선점형 스케줄러를 위해 필요

타이머 인터럽트를 발생시키는 장치가 컴퓨터 안에 칩으로 존재하는데 해당 칩에서 일정 간격으로 인터럽트를 계속해서 발생시킵니다. 여기서는 1초 주기로 발생시킨다고 가정하겠습니다.

그러면 하드웨어에서는 1초 간격으로 운영체제에게 인터럽트를 발생시키고 그것을 받은 운영체제는 해당 인터럽트를 누적시켜서 가지고 있습니다.

여기서 우리가 선점형 스케줄러를 사용한다고 가정했을때 10초마다 프로세스를 교체한다고 한다면 운영체제는 하드웨어로부터 받은 인터럽트의 누적이 10초가 되는 순간 프로세스를 교체해야 한다는 시기를 알 수 있습니다. 이것이 외부 인터럽트 처리 방법이고 선점형 스케줄러에게 필요한 기능입니다.

3. 입출력 인터럽트 | I/O Interrupt

다음은 입출력 인터럽트입니다. 그 종류도 매우 다양한텐데 기본적으로 키보드, 마우스, 저장장치, 프린터 등등 많겠지만 마우스로 예를 들면 마우스가 클릭이 될때마다 그것을 운영체제에게 알려줘야 할텐데 그것을 알려주는 인터럽트가 입출력 인터럽트라고 생각하면 됩니다.


시스템 콜 인터럽트

앞의 포스팅에서 나왔던 시스템 콜입니다. 이러한 시스템 콜도 내부적으로는 인터럽트와 같은 방식으로 동작합니다. 시스템 콜의 종류는 매우 다양하지만 open()을 예시로 들겠습니다.

open(data.txt".O_RDONLY);이라는 함수(시스템 콜)을 실행할때 해당 코드의 내부를 살펴보면 다시 말해 실제 기계어로 어떤 방식으로 컴파일이 되나 살펴보면 아래와 같습니다.

  • eax 레지스터에 시스템 콜 번호를 넣고 | (시스템 콜은 각각의 고유 번호를 가지고 있습니다.)
  • ebx 레지스터에는 시스템 콜에 해당 인자값을 넣고 | 여기서는 ("data.txt".O_RDONLY) 부분을 인자로 볼 수 있습니다.
  • 소프트웨어(내부) 인터럽트 명령을 호출하면서 0x80값을 넘겨줍니다.
mov eax, 5  // 5는 시스템 콜 고유 번호입니다.
mov ebx, 0  // 0은 인자입니다.
int 0x80 // 소프트웨어 인터럽트 명령

항상 마지막에 int 0x80 명령어가 들어가는데 CPU가 제공하는 OP codeint와 인터럽트의 번호를 넣어주는 것인데 0x80이 의미하는것이 바로 시스템 콜의 번호입니다.

인터럽트는 내부의 코드와 별개로 외부에서 실행이 되지만 해당 명령어를 통해 내부 코드안에서 강제로 실행을 시킬 수 있습니다. 다른 인터럽트와는 다르게 동작한다는 말입니다.

그렇다면 시스템 콜 인터럽트 명령을 호출하면서 0x80 값을 넘겨주면 어떤일이 일어날까요?

해당 인터럽트를 받으면 첫 번째로 CPU는 사용자 모드를 커널 모드로 변경해주는데. 위에서 얘기한 int라는 OP code가 해당 역할을 수행합니다. 그래야 시스템 자원에 접근할 수 있겠죠.

그리고 IDT(Interrupt Descriptor Table)라고 하는 주소에 접근합니다. 해당 주소에 인터럽트의 번호와 해당 번호에 맞는 코드가 들어가 있는 주소가 있기 때문입니다. 우리는 0x80 값을 넘겨주었기 때문에 그에 해당하는 주소(함수)를 찾아서 실행합니다. 그리고 해당 함수에는 system_call() 이라는 함수가 저장되어 있습니다.

즉, system_call() 함수를 실행한건데 해당 함수에서는 위에서 얘기한 eax로부터 받은 시스템 콜 번호에 해당하는 함수를 (call)호출합니다. 그리고 이때 ebx로 받은 인자 값을 같이 넘겨줍니다. 우리는 5와 (data.txt".O_RDONLY)를 넣어줬네요.

그리고 커널 모드에서 호출한 함수를 실행하게되고 실행이 완료되면 다시 사용자 모드로 변경되면서 다시 해당 프로세스의 다음 코드를 실행하게됩니다.


지금까지 나온 사용자 모드와 커널 모드 그리고 프로세스와 인터럽트를 그림으로 보면 아래와 같습니다.


프로세스는 마치 계속 실행되고 있는것럼 보이지만 내부적으로는 사용자 모드와 커널 모드를 수도없이 왔다갔다하면서 실행이 되고 있었습니다.


인터럽트 그리고 IDT(Interrupt Descriptor Table)

위에도 언급했지만 인터럽트는 각각의 고유 번호가 미리 정의되어 있습니다. 그래야 이전에 이야기했었던 수 많은 이벤트들을 각각 어떻게 처리할지 알 수 있으니까요. 그리고 수 많은 이벤트들을 어떻게 처리해야하는지에 대한 코드들이 운영체제에 구현이 되어있습니다. 그리고 해당 코드들은 IDT에 기록이 되어있습니다.

IDT에 기록이 되어 있는건 알겠는데 언제 기록을 하나? 어쨋든 해당 기록되어 있는 코드들을 사용하려면 프로세스의 실행보다 먼저 기록이 되어있어야 하기때문에 컴퓨터는 부팅시에 바로 운영체제가 해당 기록을 하기 시작합니다. 그리고 당연히 내부에서 실행하는 코드이기 때문에 커널 영역에 위치하게 됩니다.


인터럽트의 번호

리눅스의 예입니다.

인터럽트마다 고유의 번호

  • 0 ~ 31 : 예외상황 인터럽트 | 대부분 소프트웨어(내부) 인터럽트입니다.
    • 일부는 정의가 되지 않은 채로 남겨져 있습니다.
  • 32 ~ 47 : 하드웨어 인터럽트
    • 주변장치의 정류 혹은 개수에 따라 변경이 가능합니다.
  • 128 : 시스템 콜
    • 0x80(16진수)를 10진수로 전환하면 128입니다.

인터럽트 그리고 프로세스와 스케줄러

지금까지의 얘기를 간단하게 정리하자면 인터럽트는 고유의 번호가 있습니다. 그리고 해당 번호에 해당하는 코드들이 있으며 해당 코드들은 운영체제에 존재합니다. 그리고 그러한 코드와 번호들에 대한 정보가 IDT에 들어가 있다.라고 정리할 수 있겠습니다. 그래서 인터럽트가 발생하면 IDT를 참조해서 코드를 실행한다. 라고 마무리할 수 있겠네요.

프로세스

프로세스와 인터럽트를 정리하자면 인터럽트가 발생하면 프로세스는 실행을 중단하게 됩니다. 그리고 커널 모드로 변경한 뒤에 위에 얘기한대로 IDT를 찾아가 해당 인터럽트에 해당하는 코드를 커널 모드에서 실행합니다. 그리고 실행이 끝나면 다시 사용자 모드로 변경됩니다.

스케줄러 (선점형)

스케줄러와 인터럽트를 정리하자면 하드웨어에서 일정 시간마다 타이머 인터럽트를 운영체제에게 알려줍니다. 그러면 운영체제는 해당 인터럽트 정보를 가지고 카운트등을 해서 누적시켜 가지고 있겠죠. 그 뒤에 인터럽트의 카운트가 스케줄러가 정해놓은 어떠한 규칙과 일치한다면 현재 프로세스를 다른 프로세스로 교체하게 됩니다.

profile
자바스크립트를 좋아합니다. | 이유를 알고 있는 것과 모르는 것의 차이는 분명하다.

0개의 댓글