Interrupt

인터럽트
CPU가 프로그램을 실행하고 있을때, 입출력 하드웨어 등의 장치나 또는 예외상황이 발생하여 처리가 필요한 경우
CPU에 알려서 처리하는 기술

이벤트 발생에 대한 처리: 인터럽트
인터럽트가 필요한 이유
선점형 스케쥴러 구현
    프로세스가 running 중에 스케쥴러가 이를 중단시키고, 다른 프로세스로 교체하기 위해, 현재 프로세스 실행을 중단시킴
    그러기 위해서는 스케쥴러 코드가 실행이 되서, 현 프로세스 실행을 중지시켜야 한다.

IO device와의 커뮤니케이션
    저장매체에서 데이터 처리 완료시, 프로세스를 깨워야 한다.(block state->ready state)

예외 상황 핸들링
    CPU가 프로그램을 실행하고 있을 때, 입출력 하드웨어 등의 장치나 또는 예외상황이 발생할 경우,
    CPU가 해당 처리를 할 수 있도록 CPU에 알려줘야 한다.
wiki_interrupt
마이크로프로세서는 인터럽트를 감지하면 지금 실행중인 기계어 코드를 중단하고 해당 인터럽트를 위한 처리 프로그램으로 점프하여 해당 일을 수행 한다. 인터럽트 처리를 위한 루틴을 인터럽트 서비스 루틴(ISR, Interrupt Service Routine)이라고 한다. 인터럽트는 주로 하드웨어적으로 CPU 코어(CPU-core)에 입력되고, 현재 진행중인 기계어 코드가 종료되면 실행한다. 인터럽트가 접수 되었을 때, 인터럽트를 처리할 것인가는 CPU코어의 특수레지스터에 비트 마스크를 통해 선택적으로 수용한다
인터럽트 처리 예
CPU가 프로그램을 실행하고 있을 떄,
    입출력 하드웨어 등의 장치 이슈 발생
        파일 처리가 끝났다는 것을 운영체제에 알려주기
        운영체제는 해당 프로세스를 block state에서 실행 대기(ready) 상태로 프로세스 상태 변경하기

또는 예외 상황이 발생
    0으로 나누는 계산이 발생해서, 예외 발생을 운영체제에 알려주기
    운영체제가 해당 프로세스 실행 중지/에러 표시
이벤트와 인터럽트
인터럽트는 일종의 이벤트라고 불린다.
이벤트에 맞게 운영체제가 처리
주요 인터럽트
1. 계산하는 코드에서 0으로 나누는 코드 실행시(Divided by zero Interrupt)
#include <stdio.h>
int main(void){
printf("Hello world");
int data;
int divider=0;
data=1/divider; //
return 0;
}
컴파일러 레벨이 아닌 프로그램 실행 단계에서 중지/에러 발생(운영체제에서 처리)
2. 타이머 인터럽트
    선점형 스케쥴러를 위해 필요(하드웨어로 부터 일정 시간마다 타이머 인터럽트를 운영체제에 알려줌)
    ex) 100ms 마다 인터럽트 발생 후 프로세스 변경(교체)
3. 입출력(IO) 인터럽트
    프린트, 키보드, 마우스, 저장매체(SSD 등)
인터럽트 종류
내부 인터럽트
    프로그램 내부에서 잘못된 명령 또는 잘못된 데이터 사용시 발생
        0으로 나누었을 때
        사용자 모드에서 허용되지 않는 명령 또는 공간 접근시(C언어 포인터가 잘못된 주소를 지칭되었을 때)
            리눅스의 경우(0-3GB 까지 사용자 모드, 3-4GB는 커널 모드 프로세스가 실행된다.
            사용자 모드에서 커널모드로 포인터를 통해서 접근할 시, 인터럽트 발생
        계산 결과가 Overflow/Underflow 날 때
외부 인터럽트
    주로 하드웨어에서 발생하는 이벤트(프로그램 외부)
        전원이상
        기계문제
        키보드 등 IO관련 이벤트
        Timer 이벤트
내부 인터럽트는 주로 프로그램 내부에서 발생하므로, 소프트웨어 인터럽트라고 부른다.
외부 인터럽트는 주로 하드웨어에서 발생하므로, 하드웨어 인터럽트라고 한다.
시스템 콜 인터럽트
시스템 콜 실행을 위해서는 강제로 코드에 인터럽트 명령을 넣어,  CPU에 실행 시켜야 한다.
시스템 콜 실제 코드
    eax 레지스터에 시스템 콜 번호를 넣고,
    ebx 레지스터에는 시스템 콜에 해당하는 인자값을 넣는다.
    소프트웨어 인터럽트 명령을 호출하면서 0x80값을 넘겨준다.
mov eax, 1
mov ebx, 0
int 0x80 // 0x80
시스템콜 인터럽트 명령을 호출하면 0x80값을 넘겨준다.
1. CPU는 사용자 모드를 커널 모드로 바꿔준다.
2. IDT(Interrupt Descriptor Table) 에서 0x80에 해당하는 주소(함수)를 찾아서 실행
3. system_call() 함수에서 eax 로부터 시스템 콜 번호를 찾아서, 해당 번호에 맞는 시스템콜 함수로 이동
4. 해당 시스템콜 함수 실행 후, 다시 커널 모드에서 사용자 모드로 변경하고, 다시 해당 프로세스 다음 코드 진행

0x80——<int><opcode>  시스템콜 함수 주소이다.
Linux

ex) 키보드 인터럽트
키보드 입력->인터럽트 33번 CPU에 전달(int 0x33)->CPU는 처음 운영체제를 시작할 때, 할당된 IDT를 참조->
33번은 irq_desc로 이동(32-255 인터럽트는 irq_desc 참조)->33번은 irq_desc[1] call->irq_desc[1]은 atkbd 함수를 호출->
atkbd 함수 실행

ex) fork()가 실행될 때 예시
mov    eax, 2        // 시스템콜번호를 레지스터에 저장
INT     0x80
(ebx에는 인자값을 대입)
------------------------------------------------------------------
IDT[80]        // IDT 테이블 0x80으로 이동=system_call()
sys_call_table[2]        // sys_call_table 2번으로가서 실행=sys_fork()
사용자/커널 모드와 프로세스, 인터럽트
User Mode       | ProcessA                                         ProcessA                                    ProcessB
                                                  (System call)                                 (Timer Interrupt)
Kernel Mode    |                     SystemCallHandler                        ProcessScheduler
인터럽트와 IDT
인터럽트는 미리 정의되어 각각 번호와 실행 코드를 가리키는 주소가 기록되어 있다.
    IDT(Interrupt Descriptor Table)에 기록(이벤트 번호: 실행코드의 주소(함수)
    컴퓨터가 부팅시 운영체제가 기록
    운영체제 내부 코드(커널모드/커널 영역)
리눅스의 예
0-31: 예외상황 인터럽트(일부는 정의되지 않은 채로 남겨져 있다)
          내부/소프트웨어 인터럽트
32-47: 하드웨어 인터럽트(주변장치 종류/갯수에 따라 변경 가능)
128: 시스템 콜(0x80)
인터럽트와 프로세스
1. 프로세스 실행 중 인터럽트 발생
2. 현 프로세스 실행 중단
3. 인터럽트 처리 함수 실행(운영체제-커널 모드)
4. 현 프로세스 재실행
타이머 인터럽트 예
timer_interrupt{
count++;
if(count>5){
scheduler();
count=0;
}
}
5번 timer_interrupt 발생시, scheduler() 함수 실행_프로세스 변경
프로그램에서 빈번한 I/O 처리는 많은 부하를 만든다.
ex) Spark는 메모리에서 빅데이터를 처리함으로서 빠른 처리 가능