CS/운영체제

[OS] 인터럽트와 인터럽트 핸들링

primayy 2020. 11. 28. 21:24
728x90

1. 인터럽트?

하나의 CPU에서는 매 순간 하나의 프로그램만 실행시킬 수 있습니다.

그렇기 때문에 현재 CPU를 할당받아 실행되고 있는 프로그램이 I/O 처리와 같이 수행하는데 오래 걸리는 작업을 할 때에도 CPU를 점유하고 있다면, CPU라는 비싼 자원을 낭비하게 됩니다.

 

따라서 운영체제는 자원의 효율적 관리라는 가장 중요한 목표를 달성하기 위해, 인터럽트라는 방식을 사용합니다.

인터럽트(interrupt)는 단어의 뜻 그대로 현재 실행되고 있는 프로그램을 '일시 정지' 시키는 것입니다.

즉, 프로그램이 당장 처리해야 하는 작업이 발생하면, 다른 처리를 하도록 일시 정지시킨 뒤, 그동안 CPU를 다른 프로그램에게 할당하여 자원을 낭비하지 않도록 만드는 것입니다.

 

인터럽트의 발생은 어떻게 감지?

인터럽트의 발생은 CPU 옆에 있는 인터럽트 라인(Interrupt Line)이 감지합니다. 

CPU가 하나의 명령어를 실행할 때마다 인터럽트 라인을 확인하며 인터럽트가 발생했는지 확인하는 것입니다.

 

인터럽트 처리 방법?

인터럽트가 발생하면 CPU는 현재 수행 중이던 작업을 멈추고, 인터럽트와 관련된 일을 처리합니다.

해당 부분에 대한 자세한 설명은 다음에 다룰 인터럽트 핸들링에서 설명하도록 하겠습니다.

 

1.1 인터럽트의 종류

무엇이 인터럽트를 발생시켰는지에 따라 인터럽트는 다음과 같이 분류됩니다.

  • 소프트웨어 인터럽트(trap)
  • 하드웨어 인터럽트

즉, 소프트웨어에 의해서 발생한 인터럽트를 소프트웨어 인터럽트라고 하며 하드웨어에 의해 발생한 인터럽트를 하드웨어 인터럽트라고 합니다.

 

1.1.1 소프트웨어 인터럽트

소프트웨어 인터럽트는 예외상황, 시스템 콜 등이 있습니다.

  • 예외 상황
    프로세스가 0으로 나누는 연산 등 불가능한 작업을 시도하거나, 자신의 메모리 영역 바깥을 접근하려는 시도를 할 때 이에 대한 처리를 위해 발생시키는 인터럽트입니다.
  • 시스템 콜
    프로그램이 자신이 작성하지 않은 코드를 운영체제로부터 서비스받기 위해 발생시키는 인터럽트입니다. 추후에 조금 더 자세하게 다루는 글을 작성할 예정입니다.

1.1.2 하드웨어 인터럽트

컴퓨터와 연결되는 하드웨어에는 기본적으로 하드웨어의 CPU와도 같은 역할을 하는 컨트롤러와 로컬 버퍼라는 작은 메모리가 존재합니다.

 

프로그램이 하드웨어에 어떤 데이터를 요청하면, 하드웨어는 필요한 데이터를 하드웨어의 로컬 버퍼에 임시로 저장합니다.

 

그리고 로컬 버퍼에 프로그램으로 전달할 데이터가 충분히 쌓이면 프로그램으로 데이터를 전달하기 위해 CPU에 작업을 요청하는데, 이때 작업 요청을 위해 하드웨어의 컨트롤러가 인터럽트를 발생시킵니다.

이를 하드웨어 인터럽트라고 합니다.

 

즉, 하드웨어 인터럽트는 하드웨어가 요청된 데이터를 모두 준비했다고 CPU에게 알리는 방법입니다.

2. 인터럽트 핸들링

인터럽트는 소프트웨어, 또는 하드웨어에 의해서 발생한다고 말했습니다.

소프트웨어든 하드웨어든 인터럽트가 발생하면 CPU는 현재 수행하던 작업을 멈추고, 발생한 인터럽트를 처리하기 위한 절차를 거칩니다.

 

이때, 인터럽트가 발생한 경우에 처리해야 할 일의 절차를 인터럽트 핸들링이라고 합니다.

 

인터럽트 핸들링을 간단하게 요약하면 다음과 같습니다.

(프로그램 A가 현재 CPU를 할당받아서 실행되고 있다고 가정합니다.) 

 

  1. A의 현재 상태 저장
  2. 인터럽트 벡터에서 인터럽트 서비스 루틴의 주소를 찾음
  3. 인터럽트 서비스 루틴의 주소로 이동해서 서비스 루틴 실행
  4. 인터럽트 서비스 루틴이 모두 실행되면, A의 상태 복원

각 과정에 대해서 조금 더 자세하게 설명하도록 하겠습니다.

 

1. A의 현재 상태 저장

인터럽트가 발생하면 현재 실행되고 있는 프로그램 A의 상태를 저장해야 합니다.

왜냐하면 CPU는 명령어를 수행하기 위해 내부에 임시 기억 장치인 '레지스터'에 명령어를 불러와 실행시키는데,

인터럽트가 발생하면 새로운 명령어를 실행시키기 위해 레지스터에 있는 값이 초기화되기 때문입니다.

따라서 인터럽트 처리 후 인터럽트가 발생한 지점부터 프로그램을 다시 실행시키기 위해서는 현재까지 실행한 A의 상태를 저장해둬야 합니다.

 

A의 상태를 저장하기 위해서 사용하는 값들은 PCB라고 불리는 프로세스가 실행될 때 만들어지는 자료구조에 저장되어있습니다.

PCB

https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4_%EC%A0%9C%EC%96%B4_%EB%B8%94%EB%A1%9D

 

2. 인터럽트 벡터에서 인터럽트 서비스 루틴의 주소를 찾음

운영체제의 전부를 메모리에 항상 적재시키는 것은 어렵기 때문에, 우리는 운영체제의 가장 핵심적인 부분인 커널만을 메모리에 상주시켜놓습니다.

즉, 운영체제 역시 메모리에 존재하는 프로그램입니다. 

 

인터럽트 처리는 커널의 코드를 수행하는 것을 말합니다. 따라서 인터럽트를 처리하기 위해서는 커널이 실행되도록 CPU를 할당한 뒤, 커널에 존재하는 인터럽트 처리 코드의 시작 주소를 찾아야 합니다. 이 주소는 인터럽트 벡터라고 불리는 각 인터럽트 처리 코드의 시작 주소를 갖고 있는 테이블에 저장되어있습니다.

 

다시 말해, 인터럽트가 발생하면 커널에서 인터럽트를 처리하는 코드를 실행시키기 위해 처리 코드의 시작 주소가 저장되어있는 인터럽트 벡터를 참고해 주소를 찾아야 합니다.

 

3. 인터럽트 서비스 루틴의 주소로 이동해서 서비스 루틴 실행

인터럽트 서비스 루틴은 발생한 인터럽트를 실질적으로 처리하는 코드를 말합니다.

서비스 루틴 코드가 모두 실행되면 해당 인터럽트가 해결됩니다.

 

4. 인터럽트 서비스 루틴이 모두 실행되면, A의 상태 복원

인터럽트 처리가 끝나면, PCB에 저장된 프로그램 A의 다음 실행 주소를 불러온 뒤, 코드의 나머지 부분을 수행합니다.

 


알아두면 유용할 수도 있는 것들

 

1. 인터럽트 처리 중 또 다른 인터럽트가 발생한다면?

원칙적으로 인터럽트 처리 중에 또 다른 인터럽트가 발생하는 것은 허용하지 않습니다.

 

이는, 데이터의 일관성을 유지하기 위함입니다.

 

예를 들어 인터럽트가 발생해서 운영체제 커널에 정의된 데이터를 변경하고 있는 상황에 또 다른 인터럽트가 발생해서 같은 데이터를 수정하게 된다면, 데이터가 원래 의도하지 않았던 값으로 변경될 수도 있습니다.

 

그러나, 예외가 존재할 필요성도 있습니다.

 

예를 들어 현재 인터럽트 처리 루틴보다 훨씬 급하고 중요하게 처리해야 할 인터럽트가 발생하는 경우입니다.

이 때는 현재 처리 중이던 인터럽트 코드의 수행 지점을 커널 스택에 저장한 뒤, 우선적으로 처리할 인터럽트를 처리한 뒤, 이전에 실행 중이던 인터럽트 코드 부분으로 되돌아와 남은 부분을 처리하게 됩니다.

 

(계속해서 추가할 예정)


틀린 부분 또는 수정할 부분이 있다면 댓글로 알려주시면 감사드리겠습니다.

 

2020.11.28 첫 작성

 

728x90
반응형