운영체제와 인터럽트
CPU 연산과 I/O 연산에서 일어나는 인터럽트
입출력 장치들의 I/O 연산은 입출력 컨트롤러가 담당한다. 컴퓨터 내에서 수행되는 연산은 메인 CPU가 담당한다. 이때 입출력 장치와 메인 CPU는 동시에 동작이 가능하다.
각 장치마다 이를 제어하기 위해 설치된 장치 컨트롤러는 장치로부터 들어오고 나가는 데이터를 임시로 저장하기 위한 작은 메모리, 로컬 버퍼(local buffer)를 가지고 있다.
프로그램이 장치에게 어떤 명령을 내리면 그 장치의 컨트롤러가 로컬 버퍼에 명령을 수행하여 관련 메모리를 로컬 버퍼에 저장한다. 이때 CPU가 이 작업이 끝났는지를 지속적으로 체크하는 것이 아니다. 장치에 있는 컨트롤러가 인터럽트를 발생시켜 CPU에 상황을 보고한다.
이때 인터럽트란, 컨트롤러들이 CPU의 서비스가 필요할 때 이를 통보하는 방법을 말한다. 예를 들어, 키보드 입력 혹은 요청된 디스크 입출력 작업의 완료 등 CPU에 알려줄 필요가 있는 이벤트가 일어난 경우 컨트롤러가 발생시키는 것이다.
CPU는 기본적으로 매 시점 메모리에서 명령을 하나씩 읽어와서 수행한다. CPU 옆에는 인터럽트 라인(interrupt line)이 있어서, CPU는 명령 하나를 수행할 때마다 인터럽트가 발생했는지 확인한다. 인터럽트가 발생했으면 다음 명령을 수행하기 전에 인터럽트 처리를 하게 되고, 그렇지 않으면 다음 명령을 계속 수행하게 된다.
인터럽트의 일반적 기능
운영체제가 해야 할 일들을 운영체제의 개발자가 미리 프로그래밍해서 커널 내에 포함시켜뒀다. 인터럽트가 발생하면 CPU는 하는 일을 멈추고 운영체제 커널 내에서 해당 인터럽트의 처리를 위해 정의된 코드를 찾게 된다. 운영체제는 할 일을 쉽게 찾아가기 위해 인터럽트 벡터(interrupt vector)를 가지고 있다. 인터럽트 벡터란 인터럽트 종류마다 번호를 정해서, 번호에 따라 처리해야 할 코드가 위치한 부분을 가리키고 있는 자료구조를 뜻한다. 실제 처리해야 할 코드는 인터럽트 처리루틴 또는 인터럽트 핸들러라 불리는 다른 곳에 정의된다.
인터럽트에는 하드웨어 인터럽트와 소프트웨어 인터럽트가 있다. CPU의 서비스가 필요한 경우, CPU 옆에 있는 인터럽트 라인에 신호를 보내서 인터럽트가 발생했음을 알려주는 방식은 둘 다 동일하다. 하지만 하드웨어 인터럽트는 컨트롤러 등 하드웨어 장치가 CPU의 인터럽트 라인을 세팅하는 반면, 소프트웨어 인터럽트는 소프트웨어가 그 일을 수행한다는 차이점이 있다.
소프트웨어 인터럽트
소프트웨어 인터럽트는 트랩(trap)이라는 용어로 주로 불린다. 소프트웨어 인터럽트의 예로, 예외상황(exception)과 시스템 콜(system call)이 있다.
예외 상황이란, 사용자 프로그램이 0으로 나누는 연산 등 비정상적인 작업을 시도하거나, 자신의 메모리 영역 바깥에 접근하려는 시도 등 권한이 없는 작업을 시도할 때 이에 대한 처리를 위해 발생시키는 인터럽트를 말한다.
시스템 콜이란, 사용자 프로그램이 운영체제 내부에 정의된 코드를 실행하고 싶을 때 운영체제에 서비스를 요청하는 방법이라고 볼 수 있다. 사용자 프로그램 자신의 코드는 직접 CPU를 가지고 실행하지만, 사용자 프로그램에 정의되지 않고 운영체제 커널에 있는 코드를 사용하고자 할 때 인터럽트 라인 세팅을 통해 CPU 제어권을 운영체제로 넘겨 실행하게 된다.
시스템 콜이나 예외 상황 모두 사용자 프로세스로부터 CPU의 제어권이 운영체제에 이양되어 처리된다. 이 과정에서 프로그램 코드가 직접 인터럽트 라인을 세팅하는 명령을 실행하여 인터럽트를 발생시킨 후 제어권이 넘어간다. 따라서 이들도 넓은 의미에서 인터럽트의 범주에 포함시킨다.
인터럽트 핸들링
인터럽트 핸들링은 인터럽트가 발생한 경우에 처리해야 할 일의 절차를 뜻한다. 절차의 예는 다음과 같다.
- 프로그램 A가 실행되고 있을 때 인터럽트가 발생한다.
- A의 현재 상태(현재 CPU에서 실행 중인 명령의 메모리 주소를 포함해 몇 가지 부가 정보)를 먼저 저장한다. 인터럽트가 발생해 새로운 명령을 실행하면 기존 레지스터 값이 모두 지워지기 때문이다.
- 인터럽트 처리를 한다.
운영체제는 현재 시스템 내에서 실행되는 프로그램들을 관리하기 위해 프로세스 제어블록(Process Control Block, PCB)이라는 자료구조를 내장한다. PCB는 각 프로그램마다 하나씩 존재한다. PCB는 해당 프로그램의 어느 부분이 실행 중이었는지 저장한다. 구체적으로 프로그램이 실행 중이던 코드의 메모리 주소와 레지스터 값, 하드웨어 상태 등을 저장하고 있다.
다시 인터럽트 핸들링의 예를 들자면,
- 프로그램 A가 실행되고 있을 때 인터럽트가 발생한다.
- 프로그램 A의 실행 상태를 프로세스 제어블록에 저장한다.
- CPU의 제어권이 인터럽트 처리루틴으로 넘어간다.
- 인터럽트 처리를 한다.
- 인터럽트 처리가 끝난다.
- 프로세스 제어블록으로부터 프로그램 A의 실행 상태를 CPU 상에 복원해 다시 프로그램 A를 인터럽트 당하기 직전부터 실행한다.
오늘날의 컴퓨터에서 운영체제는 인터럽트가 발생할 때만 실행된다. 시스템이 부팅 후 정상 상태에 머무르면 CPU가 항상 사용자 프로그램에 의해 사용되기 때문이다. 운영체제가 직접 CPU를 점유하는 경우는 인터럽트에 의하지 않고는 발생하지 않는다.
그럼에도 불구하고 운영체제는 컴퓨터 시스템 내의 모든 하드웨어 및 소프트웨어 자원을 체계적이고 효율적으로 관리할 수 있다. → 와, 어떻게? 다음에 알아보자.