Computer Science/Operating System

[운영체제] Interrupt

HJChung 2021. 2. 9. 04:48

인터럽트란,

어느 한 순간 CPU가 실행하는 명령은 PC(Program Counter)가 가리키는 명령 하나이다. 이렇게 CPU가 프로그램을 실행하고 있을 때, 입출력 하드웨어 등의 장치와 커뮤니케이션 해야할 때나, 예외상황이 발생하여 처리가 필요할 경우가 생길 수 있다. 이 때 CPU에 알려서 처리하는 기수를 인터럽트라고 한다. 

 

인터럽트의 필요성

1. 선점형 스케쥴러를 구현

하기 위해서는 프로세스 running중에 스케쥴러가 이를 중단시키고, 다른 프로세스로 교체하기 위해, 현재 프로세스의 실행을 중단시킨다. 그러려면 스케쥴러 코드가 실행되어야 하는데, 이 때 잠시 인터럽트가 사용된다. (타이머 인터럽트)

 

2. I/O Device와의 커뮤니케이션

예를 들어, 저장 매체에서 데이터 처리가 완료됐을 때, block상태이던 프로세스를 다시 ready상태로 깨워야하는데 이 때 인터럽트가 사용된다. (입출력(IO) 인터럽트)

3. 예외 상황 핸들링

CPU가 프로그램을 실행하고 있을 때, 입출력 하드웨어 등의 장치나 예외 상황이 발생할 경우 CPU가 해당 처리를 할 수 있도록 CPU에 알려주어야 한다. 

예를 들어, 0으로 나누는 계산 코드가 발생한 경우, 이러한 연산은 불가하기 때문에 예외 발생을 운영체제에게 알려주어야 한다. 그러면 운영체제게 해당 프로세스를 실행 중지시키고, 에러를 표시해준다. (Divide-by-Zero Interrupt)

#include <stdio.h>

int main(){
	printf("Hello World!");
    int data;
    int divider = 0;
    data  = 1 / divider; //이 부분에서 인터럽트 발생
    return 0;
 }
 
 //실행 결과
 // Hello World!
 // Floating Point exception (core dumped) //운영체제의 인터러트 처리 루틴이 이런 에러 메세지를 내어준다.  

 

인터럽트 종류

1) 내부 인터럽트 (소프트웨어 인터럽트)

내부 인터럽트란 주로 프로그램 내부에서 잘못된 명령이나 잘못된 데이터 사용시 예외상황이 발생하는 것이다. 

내부 인터럽트가 발생하는 경우 예시)

- (위에서 살펴본) 0으로 나눴을 때

- 사용자 모드에서 허용되지 않은 명령 또는 공간 접근시 (시스템콜)

- 계산 결과가 Overflow/Underflow 날 때

 

2) 외부 인터럽트 (하드웨어 인터럽트)

주로 하드웨어, 프로그램 외부에서 발생되는 이벤트

외부 인터럽트가 발생하는 경우 예시)

- 전원 이상

- 기계 문제

- 키보드 등 I/O 관련 이벤트

- Timer 이벤트

 

인터럽트와 IDT

인터럽트에는 정말 여러 개가 있고, 이 여러 인터럽트는 미리 정의되어 있어서 각각 번호와 실행 코드를 가리키는 주소가 IDT(Interrupt Descriptor Table) 에 기록되어 있다. 

리눅스 OS의 경우엔, 

- 0 ~ 31: 예외 상황 인터럽트 (소프트웨어 인터럽트)

- 32 ~ 47: 하드웨어 인터럽트 (주변 장치 종류/ 갯수에 따라 변경 가능)

- 123: 시스템 콜 

 

시스템 콜 인터럽트 

시스템 콜은 사용자 모드에서 특권 명령을 실행하기 위해 커널 모드로 전환해야 할 때, 커널 모드로의 전환을 요청하는 인터페이스이다. 이러한 시스템 콜도 내부적으로 인터럽트로 실행된다. 

시스템 콜의 실제 코드)

- eax: 레지스터에서 시스템 콜 번호를 넣고

- ebx: 레지스터에는 시스템 콜에 해당하는 인자값을 넣고

- 소프트웨어 인터럽트 명령을 호출하면서 시스템 콜에 해당하는 인터럽트 번호인 0x80을 넘겨준다. 

mov eax, 1
mov ebx, 0
int 0x80 

시스템 콜 인터럽트 명령을 호출하면서 0x80값을 넘겨주면, 

CPU는 사용자 모드를 커널 모드로 바꾸고 

IDT(Interrupt Descriptor Table)에서 0x80에 해당하는 주소에 있는 함수(system_call() 함수)를 찾아서 실행한다. 

→ 그러면 system_call() 함수에서 eax로부터 시스템 콜 번호를 찾아서, 해당 번호에 맞는 시스템 콜 함수로 이동한다. 

→ 해당 시스템콜 함수 실행 후, 다시 커널 모드에서 사용자 모드로 변경되고, 다시 해당 프로세스의 다음 코드가 진행된다.