와 멀티태스킹! 인터럽트 아시는구나!
2021년 12월 27일운영체제, 임베디드시스템와 같이 백그라운드에서 항상 돌아가는 프로그램을 사용할 때는 자원을 효율적으로 쓰는 것이 매우 중요합니다. 싱글 코어의 경우, 한 번에 하나의 작업만 수행을 할 수 있습니다. 예를 들어 노래를 들으면서 게임을 하고 싶다면, 노래를 다 들을 때까지 게임을 할 수 없는 상황이 됩니다. 이러한 문제를 해결하기 위해 똑똑한 컴퓨터 과학자들이 만든 개념이 있습니다. 바로 인터럽트와 멀티태스킹입니다. 그 중 인터럽트에 대해 알아보는 시간을 가지도록 하겠습니다.
인터럽트(Interrupt)는 프로세서에 의해 처리가 필요한 일종의 비동기식 이벤트(요청)입니다. 인터럽트가 발생한다면 먼저 현재의 프로세스 상태(Context)를 저장합니다. 다음으로 이벤트를 처리하는 특수한 코드(Handler)를 실행하고, 실행이 끝난 뒤에는 저장한 프로세스 상태를 불러와 전의 일을 마저 처리하도록 합니다. 인터럽트와 비슷하지만 동기식 이벤트 방식인 예외(Exception)라는 친구도 있습니다. 인터럽트와 예외의 큰 차이점은 발생 주체가 누구냐에 따라 다릅니다. 인터럽트는 HDD, 키보드 같은 외부 디바이스에 의해 발생된 것이고, 예외는 page fault, divide error 등과 같은 프로세서 자신에 의해 발생된 것입니다. 정리하면, 인터럽트는 프로세서가 일을 하고 있는데 어떤 특수한 일의 요청이 들어와서 급히 처리해야 하는 것을 말합니다. Interrupt 이름 그 자체로 ‘방해’한다는 일을 충실히 하고 있죠?
인터럽트가 발생했을 때 처리하는 특수한 함수를 인터럽트 핸들러(Interrupt Handler) 혹은 인터럽트 서비스 루틴(Interrupt Service Routine, ISR)이라 합니다. 인터럽트 핸들러는 이벤트를 처리하는 일도 하지만 프로세서 상태를 저장/복구하는 일도 합니다. 왜냐하면 앞서 말했듯이 일을 하고 있는 도중에 요청이 들어온거라 인터럽트 핸들러 일을 끝나고 다시 돌아갈 주소와 같은 정보가 필요합니다. 이 정보를 콘텍스트(Context)라 합니다. 따라서 인터럽트가 발생 시 콘텍스트가 한 번 저장이 되고 핸들러의 일이 끝나고 나서 다시 복원이 됩니다.
인터럽트가 발생하고 어떤 인터럽트 핸들러 함수로 들어가야할지 프로세서는 모르는 상태입니다. 이를 도와주는 것이 인터럽트 벡터 테이블(Interrupt Vector Table)입니다. 인터럽트가 발생할 때 벡터 번호가 부여되며, 이 정보로 인터럽트 벡터 테이블을 보고 해당 핸들러 주소로 진입하게 됩니다. CPU 아키텍처마다 인터럽트 벡터 번호는 달라집니다. 인터럽트 벡터 테이블 목록을 살펴보면 예외는 다시 Fault, Trap, Abort 3개로 분류됩니다.
- Fault: 문제가 발생했지만 문제가 발생한 어드레스를 알 수 있고, 해당 코드의 문제를 수정하면 정상적으로 실행 가능한 예외
- Trap: 디버깅과 관련된 예외, trap이 일어난 다음 코드에 어드레스를 가르키게 된다. 즉, 핸들러 실행 후 해당 코드의 다음부터 다시 진행하게 된다.
- Abort: 심각한 문제가 발생하여 문제가 발생한 어드레스를 찾을 수 없고 실행할 수 없는 예외
인터럽트 벡터 테이블의 0~31번까지는 Divide Error, Overflow와 같은 예외들이 미리 정의되어 있습니다. 32번 뒤로는 OS가 임의로 설정할 수 있는 영역으로 인터럽트 처리나 외부 라이브러리 시스템 콜 처리로 이용됩니다.
-
인터럽트 벡터 테이블 목록(보호 모드, IA-32e 모드)
Vector Number Description Type 0 Divide Error Fault 1 Debug Exception Fault/Trap 2 NMI Interrupt Interrupt 3 Breakpoint Trap 4 Overflow Trap 5 BOUND Range Exceeded Fault 6 Invalid Opcode Fault 7 Device Not Available Fault 8 Double Fault Abort 9 Coprocessor Segment Overrun Fault 10 Invalid TSS Fault 11 Segment Not Present Fault 12 Stack-Segment Fault Fault 13 General Protection Fault 14 Page Fault Fault 15 -Intel Reserved- Fault 16 FPU Floating Point Error Fault 17 Alignment Check Fault 18 Machine Check Fault 19 SIMD Floating Point Error Abort 20-31 -Intel Revered- Fault 32-255 User Defined Interrupt
인터럽트의 매력을 제대로 느낄 최고의 방법은 임베디드 프로젝트를 진행해보는 것입니다. 아두이노, 라즈베리파이를 가지고 간단한 인터럽트 프로젝트를 시작해보는것을 추천드립니다. 인터럽트 관련 라이브러리 함수가 많으니 어렵지 않게 입문할 수 있습니다. 다음 글은 실제 임베디드의 인터럽트 코드 부분을 보는 시간을 갖도록 하겠습니다. 인터럽트 관련 용어를 요약하면서 이번 글 마치겠습니다.
- Interrupt: 프로세서에 의해 처리가 필요한 일종의 이벤트, 프로세스 일 도중 급히 처리해야 하는 요청
- Interrupt Handler(= Interrupt Service Rountine): 인터럽트가 발생 시 처리 함수
- Context: 프로세서의 상태와 관계된 레지스터 집합
- Interrupt Vector Table: 인터럽트 핸들러를 실행하기 위한 주소를 담고 있는 테이블