인터럽트Interrupt
는 CPU가 프로그램을 실행 중일때, I/O Deivce 등의 장치나 예외 상황이 발생해서 처리가 필요할 경우에 CPU에 알려서 이를 처리하는 기법이에요.
CPU는 어느 한 순간에 PCProgram Counter
가 가리키는 Code
를 실행해요. 그 중에 하드웨어 같은 장치 접근을 하는 경우 이를 처리할 때까지 프로세스는 기다려야waiting
해요. 그래야 장치를 통해 얻은 데이터를 기반으로 프로세스 로직을 수행할 수 있기 때문이죠.
인터럽트의 필요성은 장치 접근 뿐만 아니라 다양한 경우에도 필요로 해요. 무엇이 있는지 알아봐요.
프로세스가 running
중에 스케쥴러가 이를 중단시켜서 다른 프로세스로 교체하기 위해 현재 프로세스를 중단 시킬 필요가 있어요. 그러기 위해선 스케쥴러 코드가 실행되어 현 프로세스를 중지시켜야 하죠. 이때 인터럽트를 발생시켜요.
위에서 언급한 내용이에요. 저장매체에서 데이터 처리가 완료되면 프로세스를 다시 깨울 때Block
Ready
에도 인터럽트를 발생 시켜요.
CPU가 프로그램 실행 중에 I/O Device 등의 장치나 예외 상황이 발생하면 CPU가 이 상황을 처리할 수 있도록 CPU에게 알려야 할때도 사용되요. 프로세스의 Code
실행 중에 하드웨어에서의 이상 동작 등 외부에서 문제가 발생하면 이를 처리할 수 있게 하는거죠.
대표적인 예로는,
1. I/O Device에서 파일 불러오기 완료.
이 때 프로세스를Block
Ready
상태로 바꿔줍니다.
2.숫자를 0으로 나눴을 경우의 Exception 발생.
이때 운영체제와 스케줄러에게 인터럽트를 통해 Exception을 알려서 해당 프로세스를Kill
시켜요. 중지를 시키거나 에러 표시를 띄워줘요.
- 프로세스에서 파일을 불러오기를 요청해요(
Running
Waiting
).- 이후 파일 불러오기가 완료되면 운영체제에 알려요.
- 운영체제는 해당 프로세스를
Waiting
Ready
상태로 변경시켜요.- 이후 컨텍스트 스위칭
Context Switching
을 통해Ready
Running
으로 바꿔줘요.
#include <stdio.h>
int main(int argc, char* argv[]){
printf("Hello World");
int data;
int divider = 0;
data = 1 / 0; // 오웄...! 이때 인터럽트가 발생해요. 보통 빌드하면 에러를 띄워줘요.
return 0;
}
인터럽트는 일종의 이벤트로 불려요. 이벤트에 맞게 운영체제가 처리하는 것이라 보시면 되요.
인터럽트의 종류는 크게 내부 인터럽트
와 외부 인터럽트
로 나뉘어요. 이에 대해 알아봐요.
주로 프로그램 내부에서 잘못된 명령 또는 데이터를 사용할 경우 발생해요. 대표적인 예로는,
0으로 나눈 경우에요. 위에서 계속 언급되던 그거 맞아요.
리눅스로 예를 들면, 리눅스는 기본 프로세스 공간이 4GB정도 되요. 그 중 1GB는 커널 모드에 대한 메모리에요. 사용자 모드에서 허가되지 않은 모드에 접근하려고 하면 인터럽트를 발생시켜요.
계산된 결과 값이, 예를 들면 결과 값을 int
타입 변수에 초기화를 하면, 32-bit CPU 환경 기준으로 ~ 범위 외 값이 초기화되면 Overflow나 Underflow 인터럽트를 발생시켜요.
내부 인터럽트는 프로그램 내부에서 주로 발생해서 소프트웨어 인터럽트라고도 표현해요.
주로 하드웨어에서, 즉 프로그램 외부에서 발생하는 인터럽트에요.
대표적인 예로는,
1. 전원 이상
2. 기계 문제
3. 키보드 등 I/O 관련 이벤트
4. Timer 이벤트
가 있어요.
Timer 이벤트
는 선점형 스케줄러를 위해 꼭 필요해요. 일정 주기로 하드웨어가 운영체제 한테 Timer 이벤트
를 알려서 다른 프로세스를 사용 할 수 있게끔 하는거에요.
외부 인터럽트는 프로그램 외부에서 주로 발생해서 하드웨어 인터럽트라고도 표현해요.
그럼 조금 더 디테일하게 들어가봐요.
시스템 콜 실행을 위해서는 강제로 Code
에 인터럽트 명령을 넣어서 CPU에게 해당 시스템 콜을 실행시켜야 해요.
mov eax, 0x01 // 시스템 콜 번호
mov ebx, 0x00 // 인자 값
int 0x80 // 소프트웨어 인터럽트 명령(CPU op code + 인터럽트 번호)
위 언어는 CPU에게 명령을 내리는 어셈블리어에요. 조금 더 설명하자면,
eax
레지스터에 호출할 시스템 콜 번호를 넣어요( 각 시스템 콜은 번호가 등록되어 있어요). 어떤 시스템 콜을 호출하는지를 여기서 초기화해요.ebx
레지스터에는 시스템 콜에 해당하는 인자 값을 넣어줘요.open
함수로 보면O_READONLY
가 여기에 속해요.int
라는 소프트웨어 인터럽트 명령을 호출하면서0x80
값을 넣어요. 여기서int
는 프로그래밍 언어의 그 int는 아니에요. 정확하겐CPU Opcode(명령 코드)
에요.0x80
는 인터럽트 정보를 관리하는 Interrupt Descriptor TableIDT
에서system_call()
이라는 함수가 있는 주소Code
를 의미해요.
3
에 대한 내용을 좀 더 알아보자면, 시스템 콜 인터럽트 명령을 호출하면서 0x80(Opcode)
을 CPU에 넘겨줘요. 그러면서,
- CPU는 사용자 모드를 커널 모드로 바꿔줘요.
IDT
에서0x80
에 해당하는 주소Code
(함수)를 찾아내 실행해요. 이 때eax
와ebx
값도 함께 넣어줘요.- 이 때
eax
레지스터를 확인해서 시스템 콜 번호를 확인하고, 이 번호에 해당하는 시스템 콜을 호출해요.- 그리고
ebx
에서 시스템 콜 함수의 인자를 넘겨주고 함수를 실행해요.- 함수의 결과를 프로세스에 보내고 커널 모드에서 다시 사용자 모드로 돌아가 프로세스를 실행해요.
IDT
에 관리되는 시스템 콜 함수는 매애애우 많지만, 간단하게 추려내면 아래와 같아요.
정리해서 말씀드리자면,
- 프로세스가 실행되요.
- 실행 도중에 시스템 콜이 호출됐어요!
int 0x80
mov eax, 시스템 콜 번호
mov ebx, 시스템 콜 인자 값
순으로 CPU에게 인터럽트를 알려요.- 이후 작업이 완료되면 다시 프로세스를 재개해요.
- 선점형 스케줄러로 인해 타이머가 만료되면 프로세스가
Running
Waiting
상태로 돌아가고, 다음 프로세스를 실행해요.
하나의 인터럽트 흐름을 자세히 보면 아래와 같아요.
IDT
와의 상관관계에 대해 알아봐요. 인터럽트는 IDT
에 컴퓨터가 부팅되었을 때 운영체제가 미리 정의해서 각 번호와 실행 코드를 가리키는 주소가 기록되어 있어요. 사하원칙(?)으로 말씀드리면 아래와 같아요.
- Where : IDT
- Who : 운영체제
- Whene : 컴퓨터 부팅
- What :
Code
의 주소
IDT
에 정의된 인터럽트 정보는 운영체제에 따라 달라요. 일단 리눅스에 대해서 말씀드리면
인터럽트에 대하여 다뤄봤어요. 단순히 강의 내용 기록한 걸 정리하는 것도 제법 일이네요 ㅎㅎ. 그래도 복기하는 느낌으로 하니 지식이 견고해지는 느낌이 들어요. 여기까지입니다. 추가로 수정할 일 있으면 계속 업데이트할게요.