인터럽트(Interrupt) 정의
: 프로세서가 실행하는 명령어의 순서를 바꾸는 사건
인터럽트는 흔히 동기적(Synchronous)인 인터럽트와 비동기적(Asynchronous)인 인터럽트로 나뉩니다.
동기적 인터럽트는 CPU가 명령어를 실행하는 도중에 발생하는데, 실행중인 명령어의 실행을 마친 뒤에 이를 발생시킵니다. 보통 ‘예외’라고 명시합니다. 프로그래밍을 할때 예외처리 부분을 구현하는데, 이처럼 에러나 커널이 처리해야 비정상적인 상황에 CPU가 이 상황에 대처하기 위한 작업들입니다.
비동기적 인터럽트는 다른 하드웨어 장치가 CPU의 클록 신호와는 상관없이 즉각적으로 발생시킵니다. 보통 인터럽트라하면 이 비동기적 인터럽트를 말합니다. 입출력장치에 의한 경우와 같이 외부의 돌발적인 상황이 발생하였을 경우 CPU는 실행중인 작업들을 일시 중지하고 정해진 인터럽트 작업을 수행하게 됩니다.
인터럽트의 분류
- Maskable Interrupt :
Interrupt Mask가 가능한 인터럽트, 인트럽트 마스크는 인터럽트가 발생하였을 때 요구를 받아들일지 받아들이지 않을지를 지정하는 것입니다. 즉, 마스크 가능한 인터럽트라는 것은 도중에 다른 인터럽트가 발생하였을 경우 CPU는 이를 수용할수도 않할수도 있는 인터럽트 입니다. 입출력 장치가 제기하는 인터럽트 요청과 거의 대부분의 인터럽트가 여기에 해당됩니다. - Nonmaskable Interrupt: Interrupt Mask가 불가능한 인터럽트, 몇몇 심각한 사건(예를 들면 정전이나, 하드웨어 고장과 같은 어찌할 수 없는 오류)만이 이 마스크 불가능한 인터럽트를 발생시킵니다.
예외 분류
프로세서가 감지하는 예외
: CPU가 명령어를 실행하다가 비정상적인 상황을 감지할 때 발생. cpu가 예외를 발생시킬 때 커널 모드 스택에 저장장하는 eip 레지스터의 값에 따라 3분류로 나뉩니다.
-
Faults : 저장된 eip 값은 폴트를 일으킨 명령어의 주소이기 때문에 일반적으로 고칠 수 있으며, 고쳐지면 해당 프로그램은 재시작되어 하던 작업을 이어서 수행할 수 있습니다. 자주 볼수 있는 ‘페이지 폴트’가 이에 해당됩니다.
-
Traps : 트랩을 발생시키는 명령어를 실행하자마자 발생합니다. 프로그램은 커널로부터 제어권을 다시 돌려 받은 후에 이어서 실행할 수 있습니다. eip에는 트랩을 발생시킨 명령어 다음에 실행해야 하는 명령어의 주소입니다. 즉, 트랩은 종료한 명령어가 다시 이어서 수행될 필요가 없을때 사용됩니다. 트랩의 주용도는 디버깅입니다. 디버깅할때 braking point가 이해 해당됩니다.
-
Aborts(중단) : 하드웨어 고장이나 시스템 테이블에 잘못된 값이 들어가 있는 경우가 같이 심간한 오류 상황이 생겼을때 발생합니다. eip 레지스터에 예외를 일으킨 정확한 위치를 저장하지 못하는 경우가 많습니다. 이 예외가 발생하였을 경우에는 문제가 발생한 프로세스를 강제로 종료하는 수 밖에 없습니다.
프로그래밍에 의한 예외
: 프로그래머의 요청이 int, int3 명령어에 의해 발생합니다.
Overflow나 Bound(주소범위 조사)명령어에서 프로그래밍에 의해서 예외를 발생시킵니다. cpu는 이 예외를 Traps로 처리합니다. 이 예외의 일반적인 용도는 시스템 콜을 구현하거나 디버거에 특별한 사건이 발생하였음을 알리기 위한 용도로 사용됩니다.
인터럽트가 수행되는 과정
장치에서 인터럽트 신호발생
- IC(Inturrupt Controller)에서 신호를 감지
- IC의 입출력 포트에 벡터를 저장하여 cpu가 데이터 버스를 통해 이를 읽을 수 있도록 한다.
- IC는 cpu의 INTR핀으로 발생한 신호를 보낸다. 즉 인터럽트를 발생시킴.
- cpu는 IC의 입출력 포트 중 하나에 값을 써넣어 cpu가 인터럽트 신호를 받았음을 알려준다.
- 마지막으로 INTR선의 값을 0으로 만든다.
80x86 마이크로프로세서의 예외들
IDT(Interrupt Descriptor Table)
이 시스템 테이블은 각 인터럽트와 인터럽트 핸들러의 주소, 예외 벡터와 예외 핸들러의 주소를 연결합니다. 각 엔트리는 인터럽트 벡터 또는 예외 벡터 하나에 대응합니다. IDT에는 세 종류의 이스크립터가 들어갈 수 있습니다.
-
Task gate
인터럽트 신호가 발생할 때 현재 프로세스를 대체할 프로세스의 TSS 셀렉터를 가진다. -
Interrupt gate
인터럽트나 예외 핸들러의 세그먼트 셀렉터와 세그먼트 내 오프셋을 가진다. 해당 세그먼트로 ㅈ제어를 넘길 때 프로세서는 IF 플래그를 0으로 만들어 마스크 가능한 인터럽트가 더는 발생하지 않게 한다. -
Trap gate
인터럽트 게이트와 비슷하지만 해당 세그먼트로 제어를 넘길 때 프로세서가 IF플래그를 바꾸지 않는다는 차이점이 있다.
인터럽트와 예외의 처리 과정
인터럽트 수행 과정
- 발생한 인터럽트나 예외의 해당하는 벡터 i(8bit이므로 0 <= i <= 255)값을 구한다.
- idtr 레지스터가 가리키는 IDT에서 i번째 엔트리를 읽어 들인다.
- gdtr 레지스터에서 GDT의 기본 주소를 가져와서 IDT 엔트리에 있는 셀렉터가 가리키는 세그먼트 디스크립터를 GDT에서 읽어 들인다. 디스크립터는 인터럽트 핸들러나 예외 해늗ㄹ러나 예외 핸들러를 포함한 세그먼트의 시작 주소를 지정한다.
- 인터럽트가 발생한 곳이 인증된 곳인지 검사한다. 먼저 cs 레지스터의 마지막 두 비트에 들어 있는 CPL(Current Privilege Level, 현재 특권 수준)과 GDT에 들어있는 세그먼트 디스크립터의 DPI(Descriptor Privilege Level, 디스크립터 특권 수준)을 비교한다. 인터럽트 핸들러는 인터럽트를 발생시킨 프로그램보다 낮은 특권을 가질 수 없으므로 CPL이 DPI보다 낮으면 ‘일반보호(General Protection)’을 발생시킨다.
-
선택한 세그먼트 디스크립터의 DPL과 CPL이 다른지 검사한다. 만약 다르다면 제어 유닛은 새로운 특권 수준에 맞는 스택을 사용해야 한다
-
current 프로세스의 TSS 세그먼트에 접근하기위해 tr 레지스터를 읽어 들인다.
-
새로운 특권 수준과 관련된 올바른 스택 세그먼트와 스택 포인터의 값으로 ss와 esp 레지스터를 설정한다. 이 값은 TSS에 들어있다.
-
이전 특권 수준과 연계된 스택의 논리 주소 ss와 esp의 이전 값을 새로운 스택에 저장한다.
-
- Fault가 발생했다면 예외를 발생시킨 명령어의 논리 주소로 cs와 eip를 설정하여 명령어를 다시 실행 할 수 있게 한다.
- efags와 cs, eip의 내용을 스택에 저장한다.
- 예외에 따른 하드웨어 에러 코드가 있으면 스택에 저장한다.
- cs와 eip를 각각 IDT의 i번째 엔트리에 저장된 게이트 디스크립터의 세그먼트 셀렉터와 오프셋 필드로 설정한다. 이 값은 인터럽트 핸들러나 예외 핸들러의 첫 번째 명령어의 논리 주소이다.
인터럽트나 예외를 처리하고 나면 제어권을 원래 프로세스로 돌려주어야 한다.
제어권 이양 (iret)
- 스택에 저장했던 값으로 cs와 eip, eflags 레지스터를 복구한다. 스택에서 eip 다음에 하드웨어 에러 코드가 들어 있다면 iret를 실행하기 전에 이를 꺼내야 한다.
- 핸들러의 CPLL이 cs의 하위 두 비트에 들어 있는 값과 같은지 검사한다. (만약 같다면 인터럽트 되었던 프로세스가 핸들러와 동일한 특권 수준에서 실행 중이었다는 것을 의미한다) 값이 같으면 iret는 실행을 종료하고 그렇지 않으면 다음 단계로 진행한다.
- 스택에서 ss와 esp 레지스터를 가져와서 이전 특권 수준과 연계된 스택으로 돌아간다.
- ds와 es, fs, gs, 세그먼트 레지스터의 값을 검사한다. 이 중 하나라도 DPL이 CPL보다 낮은 세그먼트 디스크립터를 가리키면 해당 세그먼트 레지스터를 지운다. 제어 유닛이 이런 일을 하는 이유는 CPL이 3인 사용자 모드 프로그램이 이전에 커널 코드에서 사용한(DPL이 0인) 세그먼트 레지스터를 사용하는 것을 막기 위함이다. 레지스터를 지우지 않으면 악의적인 사용자가 이를 이용하여 커널 주소 공간에 접근할 수 있다.
Exception Handing
프로세스에 비정상적인 상황을 알려주기 위한 목적의 시그널을 송신하기 위해 필요.
예외처리를 위한 레지스터 저장
중요 함수들
trap_init() : 예외에 관한 모드 IDT entry에 최종적인 값, 즉 예외를 처리하는 함수들을 지정.
set_trap_gate(0, ÷_error);
set_trap_gate(1, &debug);
set_intr_gate(2, &nmi);
set_system_intr(3, &int3);
set_system_gate(4, &overflow);
set_system_gate(5, &bound);
set_trap_gate(6, &invalid_op);
set_trap_gate(7, &device_not_available);
set_task_gate(8, &31);
set_trap_gate(9, &coprocessor_segment_overrun);
set_trap_gate(10, &invalid_TSS);
set_trap_gate(11, &segment_not_present);
set_trap_gate(12, &general_protection);
set_intr_gate(14, &page_fault);
set_trap_gate(15, &coprocessor_error);
set_trap_gate(17, &alignment_check);
set_trap_gate(18, &machine_check);
set_trap_gate(19, &simd_coprocessor_error);
set_system_gate(128, &system_call);
위의 함수들을 trap_int()에서 호출함으로써 예외를 처리하는 함수들을 지정.
ret_from_exception() : 스택에 저장한 사용자 모드 레지스터 주소와 하드웨어 에러 코드를 스택에서 꺼낸 후 jmp 명령어 수행
Interrupt Handiling
인터럽트는 관련된 프로세스가 중단된 후 한참 후 또는 관련 없는 프로세스가 실행 중일 경우 시그널이 도착하는 경우가 많음
-> 현재 수행중인 프로세스에 무작정 시그널을 보내면 안됨
- 입출력 인터럽트 핸들링
- IRQ선의 상태를 나타내는 주요 디스크립터 사이의 관계도