ncloader  0.1
 모두 데이타 구조 파일들 함수 변수 타입정의 열거형 타입 열거형 멤버 매크로 그룹들 페이지들
Interrupt Service Routines

Interrupt Service Routine. 더 자세히 ...

+ Interrupt Service Routines에 대한 협력 다이어그램:

모듈

 8259 Programmable Interrupt Controller
 PIC 를 초기화 한다.
 

데이타 구조

struct  task_gate
 Task gate descriptor. 더 자세히 ...
 
struct  interrupt_gate
 Interrupt gate descriptor. 더 자세히 ...
 
struct  idtr
 Interrupt descriptor table register. 더 자세히 ...
 
struct  error_code
 [Stage 2 IDTR] 더 자세히 ...
 
struct  pt_regs
 [Stage 2 Error Code] 더 자세히 ...
 
struct  pt_regs_user
 유저 레벨(Ring3) 에서 발생한 인터럽트인 경우 스택 구성의 일부 더 자세히 ...
 
struct  pt_regs_kernel
 쓰레드가 종료될 때 Clean up을 해야 하는데, 그 때 불리는 함수가 하게 될 스택 프레임을 구성한다. exit_frame_pointer 에는 EBP 의 값이 저장되고, EBP 는 exit_frame_pointer 가 있는 주소를 가리키게 된다. 이 주소를 기준으로 인자에 접근한다. 더 자세히 ...
 

매크로

#define asmlinkage   __attribute__((regparm(0)))
 
#define LOW_PRIORITY   0x7FFFFFFF
 IRQ 등록시 가장 낮은 우선 순위로, 가장 나중에 실행 된다. 더 자세히 ...
 
#define HIGH_PRIORITY   0x80000000
 IRQ 등록시 가장 높은 우선 순위로, 가장 먼저 실행 된다. 더 자세히 ...
 
#define NORMAL_PRIORITY   0
 IRQ 등록시 일반 적인 우선 순위를 정의한다. 더 자세히 ...
 

열거형 타입

enum  irq_nr {
  IRQ_NR_DIVIDE = 0x00, IRQ_NR_DEBUG = 0x01, IRQ_NR_NMI = 0x02, IRQ_NR_BREAKPOINT = 0x03,
  IRQ_NR_OVERFLOW = 0x04, IRQ_NR_BOUND = 0x05, IRQ_NR_OPCODE = 0x06, IRQ_NR_DEVICE = 0x07,
  IRQ_NR_DOUBLE_FAULT = 0x08, IRQ_NR_COPROCESSOR = 0x09, IRQ_NR_INVALID_TSS = 0x0A, IRQ_NR_SEGMENT_NOT_EXIST = 0x0B,
  IRQ_NR_STACK_FAULT = 0x0C, IRQ_NR_PROTECTION = 0x0D, IRQ_NR_PAGE_FAULT = 0x0E, IRQ_NR_MATH = 0x10,
  IRQ_NR_ALIGNMENT = 0x11, IRQ_NR_MACHINE_CHECK = 0x12, IRQ_NR_SIMD = 0x13, IRQ_NR_TIMER = 0x20,
  IRQ_NR_KEYBOARD = 0x21, IRQ_NR_IRQ2 = 0x22, IRQ_NR_SERIAL_EVEN = 0x23, IRQ_NR_SERIAL_ODD = 0x24,
  IRQ_NR_FDC = 0x26, IRQ_NR_RTC = 0x28, IRQ_NR_BINDER = 0x29, IRQ_NR_HDC = 0x2E,
  IRQ_NR_SW = 0x31, IRQ_MAX = 0x100
}
 
enum  gate_size { SIZE16 = 0, SIZE32 = 1 }
 크기 더 자세히 ...
 
enum  idt_type { TASK = 0x05, INTERRUPT = 0x06, TRAP = 0x07 }
 인터럽트 디스크립터의 종류 더 자세히 ...
 

함수

asmlinkage struct pt_regsdo_irq (struct pt_regs *ret)
 인터럽트 요청을 처리하는 함수 더 자세히 ...
 
void isr_divide_error_entry (void)
 나눗셈 익셉션 핸들러 더 자세히 ...
 
void isr_debug_entry (void)
 디버그 익셉션 핸들러 더 자세히 ...
 
void isr_nmi_interrupt_entry (void)
 Non-maskable-interrupt 인터럽트 핸들러 더 자세히 ...
 
void isr_breakpoint_entry (void)
 Break point 익셉션 핸들러 더 자세히 ...
 
void isr_overflow_entry (void)
 오버플로우 익셉션 핸들러 더 자세히 ...
 
void isr_bound_entry (void)
 bound 익셉션 핸들러 더 자세히 ...
 
void isr_invalid_opcode_entry (void)
 잘못된 opcode 익셉션 핸들러 더 자세히 ...
 
void isr_device_not_avail_entry (void)
 장치가 없는 경우의 익셉션 핸들러 더 자세히 ...
 
void isr_double_fault_entry (void)
 더블폴트 핸들러 더 자세히 ...
 
void isr_coprocessor_entry (void)
 코프로세서 핸들러 더 자세히 ...
 
void isr_invalid_tss_entry (void)
 유효하지 않은 TSS 핸들러 더 자세히 ...
 
void isr_segment_not_present_entry (void)
 세그먼트가 없는 경우에 대한 핸들러 더 자세히 ...
 
void isr_stack_segment_fault_entry (void)
 스택 세그먼트 폴트 핸들러 더 자세히 ...
 
void isr_general_protection_entry (void)
 보호 에러 핸들러 더 자세히 ...
 
void isr_page_fault_entry (void)
 페이지 폴트 핸들러 더 자세히 ...
 
void isr_math_fault_entry (void)
 math fault 핸들러 더 자세히 ...
 
void isr_alignment_check_entry (void)
 Alignment check 핸들러 더 자세히 ...
 
void isr_machine_check_entry (void)
 Machine check 핸들러 더 자세히 ...
 
void isr_simd_floating_entry (void)
 SIMD Floating 핸들러 더 자세히 ...
 
void isr_timer_entry (void)
 타이머 핸들러 더 자세히 ...
 
void isr_keyboard_entry (void)
 키보드 핸들러 더 자세히 ...
 
void isr_rtc_entry (void)
 RTC 핸들러 더 자세히 ...
 
void isr_irq2_entry (void)
 IRQ2 핸들러 더 자세히 ...
 
void isr_dummy_entry (void)
 더미 핸들러 더 자세히 ...
 
void isr_hdc_entry (void)
 HDC 핸들러 더 자세히 ...
 
void isr_fdc_entry (void)
 FDC 핸들러 더 자세히 ...
 
void isr_irq_binder_entry (void)
 IRQ Binder 핸들러 더 자세히 ...
 
void isr_sw_entry (void)
 소프트웨어 인터럽트 핸들러 더 자세히 ...
 
void enable_interrupt (void)
 인터럽트를 활성화 시킨다. 더 자세히 ...
 
void disable_interrupt (void)
 인터럽트를 비활성화 시킨다. 더 자세히 ...
 
int register_irq (int idx, int priority, int(*handler)(void *), void *data)
 인터럽트 핸들러를 등록한다. 더 자세히 ...
 
int unregister_irq (int idx, int(*handler)(void *), void *data)
 등록된 인터럽트 핸들러를 제거한다. 더 자세히 ...
 
void irq_local_restore (unsigned long value)
 주어진 인자의 값으로 인터럽트 상태를 복원한다. 더 자세히 ...
 
void irq_local_save (unsigned long *value)
 주어진 인자에 현재 인터럽트 상태를 저장하고 인터럽트를 비활성화 시킨다. 더 자세히 ...
 

변수

unsigned long kernel_cs
 커널 코드 세그먼트 디스크립터 offset 더 자세히 ...
 
unsigned long kernel_ds
 커널 데이터 세그먼트 디스크립터 offset 더 자세히 ...
 
unsigned long kernel_ss
 커널 스택 세그먼트 디스크립터 offset 더 자세히 ...
 
struct task_gate __PACKED
 

상세한 설명

Interrupt Service Routine.

인터럽트 관련 아키텍쳐 독립적 인터페이스 정의

작성자
Sung-jae Park nices.nosp@m.j@ni.nosp@m.cesj..nosp@m.com
날짜
2011-7-28 Initial writing
2013-11-10 Review writing

x86 에서는 인터럽트에 대한 설명과 함께 관련 인터럽트 서비스 루틴의 시작 주소를 인터럽트 디스크립터 테이블을 통해 관리한다. 시스템 소프트웨어 개발자는 인터럽트 디스크립터 테이블을 구성한 후 CPU 에게 알려주기면 하면 되는 것이다. 인터럽트 디스크립터 테이블의 각 엔트리는 아래와 같다.

IDT.jpg
인터럽트 디스크립터 테이블

이 테이블을 CPU 에게 알려주기 위해서는 lidt 라는 명령을 사용한다. Load Interrupt Descriptor Table 의 약자이다. 여기서 어떤 독자는 혼란 스러울 수 있는데, CPU 에게 알려주는 것이면 Store 하는 것이 아니냐 이다. 하지만 이 명령은 CPU 의 입장에서 바라보는 것이므로 Load 라고 하는 것이다. 반대로 sidt 라는 명령은 현재 CPU 가 가지고 있는 IDT 정보를 읽어 오는 것이다.

IDTR.jpg
인터럽트 디스크립터 테이블 레지스터
struct idtr {
unsigned short limit;
unsigned long base;

우리가 추가해 주어야 하는 인터럽트 목록이다.

interrupts.png
인터럽트 목록

하지만 뭔가 아쉬운 것이 있다. IDT 를 아무리 뒤져봐도 Timer interrupt handler 가 없다는 것이다. 그럼 이 Interrupt handler 와 관련된 정보는 어디서 찾아 볼 수 있을까?

그것이 바로 PIC 라고 하는 것과 관련된 것이다.

8259 Programmable Interrupt Controller

PIC 를 초기화 하는 과정에서 Interrupt 발생 번호를 조정했었다. 앞에 설명된 기본 IDT 테이블 이후의 번호에 PIC 와 관련된 장치들로 부터 발생하는 인터럽트들이 호출될 수 있도록 재조정 했다는 것이다.

pic_interrupts.png
PIC 에서 제공되는 인터럽트 종류와 설명

이 인터럽트들은 값이 조정이 되어 0x20 부터 IRQ0 가 remapping 된다. PIC 와 관련된 자세한 설명은 8259 Programmable Interrupt Controller 페이지를 참조하기 바란다.

이렇게 H/W 로 부터 발생하는 Interrupt 와 CPU 로 부터 발생하는 Interrupt 의 차이로는, Error Code 가 있다.

struct error_code {
unsigned long ext:1;
unsigned long idt:1;
unsigned long ti:1;
unsigned long selector:29;

Error Code 는 CPU 로 부터 발생하는 Interrupt 에만 존재한다. 이 경우, Interrupt Service Routine 이 호출 될 때 Stack 에 Error Code 가 push 되기 때문에, 궁극적으로, CPU 에서 발생한 Interrupt 인지 H/W 로 부터 발생한 Interrupt 인지에 따라, Stack 에 차이가 발생하게 된다. 그러므로, H/W 로 부터 발생한 Interrupt 인 경우, Interrupt Service Routine 에서 Error Code 를 push 하여, Stack 이 같은 모양이 되도록 맞춰 준다. 이렇게 하는 이유는 Interrupt Handler 를 일관되게 처리하기 위해서이다.

%macro ISR_ENTRY_WITH_ECODE 2
%1:
cli
push 0
push %2
jmp irq_router
%endmacro
%macro ISR_ENTRY 2
%1:
cli
push %2
jmp irq_router
%endmacro

이렇게 Stack 의 모양이 같아 지도록 맞춘 후에, 한 가지 더 해야 할 것이 있다. 그것은 다음장의 "CPU Context 관리" 에서 확인해보기로 한다.

  1. CPU Context 관리 두번째로 우리가 관리 해주어야 할 것은 인터럽트가 발생했을 때 CPU 가 가지고 있는 현재 정보들을 어디에 저장할 것이며, 스택은 어떻게 구성해줄지에 대한 것이다.

아래 두 그림은 특권레벨(RING)의 변화가 있을 때와 없을 때의 스택의 변화를 보여주고 있다.

NoRingChangeStack.png
스택의 변화(1)
RingChangeStack.png
스택의 변화(2)

Stack 의 변화가 Ring 에 따라서 달라지지만, 앞서 말한 것 처럼, CPU 인지 H/W 인지에 따라서도 달라진다. 일단 Interrupt Service Routine 이 들어오면, 한가지 더 해줘야 할 것이 있다. 그것이 바로 CPU Context 를 유지하는 일이다.

irq_router:
; ecode
; vector
push gs
push fs
push es
push ds
pushad ; Save all registers
; reload segements
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ; Push current Stack pointer as the first arg for do_irq
call do_irq
mov esp, eax
popad
pop ds
pop es
pop fs
pop gs
add esp, 4 ; Skip the vector number
add esp, 4 ; Skip the error code
iretd

이렇게 구현한 Service Router 를 살펴 보면, Segement Selector 와 각종 Register 들을 Stack 에 Push 하는 것을 볼 수 있다. 이렇게 하는 이유는 Interrupt Service 를 마치고 원래 하던 루틴으로 복귀 할 때, Service Routine 을 수행 하면서 CPU Register 들이 변경되는 경우, 원래 루틴으로 복귀 한 후에 Register 에 저장했던 값들이 바뀌는 문제를 해결하기 위해서 이다.

인터럽트 디스크립터를 모두 채워 넣었다면, TSS 설정도 확인해야 한다. 부트로더에서 User level 과 Kernel level 간의 전환은 없을 예정이지만, NCKernel 에서는 사용할 예정이므로 간략히 확인하고 넘어가자.

SyscallStack.png
시스템콜 서비스 루틴의 스택

컨텍스트가 바뀔 때, EFLAGS 도 다시 설정된다. EFLAGS 에 Interrupt 와 관련된 Bit flag 가 있다.

eflags_register.png

인터럽트 핸들러를 등록할 때, x86 인 경우 naked function 을 GCC 에서 구현할 수 없기 때문에 assembly language 로 ISR 진입함수를 작성하고, 그 함수에서 C 언어로 구현된 함수를 호출하도록 한다.

모든 interrupt 는 do_irq 라는 하나의 함수로 진입하도록 하고, 대신 인터럽트 발생 번호를 인자로 전달하여 알맞은 처리를 할 수 있도록 한다.

이렇게 처리하는 경우, 시스템 콜을 처리하는 것도 동일하게 처리할 수 있으므로 향 후 구현 및 관리가 쉬워지는 이점이 있다.

하지만, 분기를 한 번 더 해야 한다는 단점이 있다.

직관적인 방법 Interrupt -> ISR_ENTRY(asm) -> ISR handlers(C)

현재 구현된 방법 Interrupt -> ISR_ENTRY(asm) -> ISR_Router(asm) -> unified ISR handler(C)

do_irq 함수에서는 register_irq 를 통해 등록된 Interrupt service routine 들이 실행된다.

x86 에서는 Programmable Interrupt Controller 에 연결된 장치로 부터 발생한 Interrupt 인 경우는 그 종료를 알려 주어야 한다. 이 함수는 모든 서비스 루틴이 실행된 후에 실행 될 수 있도록 그 순서를 보장 받아야 한다. 이를 위해서 register_irq 함수는 priority 를 가진다.

priority 에 따라, 등록된 ISR 들이 실행되는 순서가 정해지며, 가장 낮은 값이 가장 먼저 실행된다.

작성자
Sung-jae Park nices.nosp@m.j@ni.nosp@m.cesj..nosp@m.com
날짜
2011-7-22
  1. Interrupt 란 무엇인가 CPU 는 Bus 또는 In/Out port 를 통해 다양한 외부 장치와 연결이 되어 있다. 데이터를 주고 받는 방법과 유사하게 어떤 변화나 이벤트에 대해서도 CPU 는 알아야 한다. CPU 가 외부로 부터의 변화를 감지할 수 있는 방법은 외부 장치를 꾸준히 감시하는 방법과 외부 장치가 변화가 생겼을 때 CPU 에게 알려주는 방법이 있다. 전자처럼 꾸준히 감시하는 방법을 Polling 이라고 하며, 후자처럼 외부 장치가 변화를 CPU 에게 알려주는 방법을 인터럽트라 한다. CPU 는 인터럽트가 발생하면 현재 하던일을 잠시 중단하고, Interrupt 발생 원인을 파악한 후 등록된 인터럽트 서비스 루틴을 실행시켜준다.

매크로 문서화

#define asmlinkage   __attribute__((regparm(0)))
#define HIGH_PRIORITY   0x80000000

IRQ 등록시 가장 높은 우선 순위로, 가장 먼저 실행 된다.

#define LOW_PRIORITY   0x7FFFFFFF

IRQ 등록시 가장 낮은 우선 순위로, 가장 나중에 실행 된다.

#define NORMAL_PRIORITY   0

IRQ 등록시 일반 적인 우선 순위를 정의한다.

열거형 타입 문서화

enum gate_size

크기

열거형 멤버
SIZE16 

16 bits

SIZE32 

32 bits

enum idt_type

인터럽트 디스크립터의 종류

열거형 멤버
TASK 

Task

INTERRUPT 

Interrupt

TRAP 

Trap

enum irq_nr
열거형 멤버
IRQ_NR_DIVIDE 
IRQ_NR_DEBUG 
IRQ_NR_NMI 
IRQ_NR_BREAKPOINT 
IRQ_NR_OVERFLOW 
IRQ_NR_BOUND 
IRQ_NR_OPCODE 
IRQ_NR_DEVICE 
IRQ_NR_DOUBLE_FAULT 
IRQ_NR_COPROCESSOR 
IRQ_NR_INVALID_TSS 
IRQ_NR_SEGMENT_NOT_EXIST 
IRQ_NR_STACK_FAULT 
IRQ_NR_PROTECTION 
IRQ_NR_PAGE_FAULT 
IRQ_NR_MATH 
IRQ_NR_ALIGNMENT 
IRQ_NR_MACHINE_CHECK 
IRQ_NR_SIMD 
IRQ_NR_TIMER 
IRQ_NR_KEYBOARD 
IRQ_NR_IRQ2 
IRQ_NR_SERIAL_EVEN 
IRQ_NR_SERIAL_ODD 
IRQ_NR_FDC 
IRQ_NR_RTC 
IRQ_NR_BINDER 
IRQ_NR_HDC 
IRQ_NR_SW 
IRQ_MAX 

함수 문서화

void disable_interrupt ( void  )

인터럽트를 비활성화 시킨다.

+ 이 함수를 호출하는 함수들에 대한 그래프입니다.:

asmlinkage struct pt_regs* do_irq ( struct pt_regs ret)

인터럽트 요청을 처리하는 함수

매개변수
[in]ret인터럽트 요청이 발생했을 때 CPU 의 Context
반환값
struct pt_regs * 인터럽트 처리 후 재 설정할 CPU 의 Context
주의
등록된 IRQ 서비스 목록을 찾는다.
각 Handler 들을 실행 시킨다 이 때, pos 와 n 변수는 재 사용한다.

+ 이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:

void enable_interrupt ( void  )

인터럽트를 활성화 시킨다.

+ 이 함수를 호출하는 함수들에 대한 그래프입니다.:

void irq_local_restore ( unsigned long  value)

주어진 인자의 값으로 인터럽트 상태를 복원한다.

매개변수
[in]value저장된 인터럽트 상태값.
반환값
void 없음
참고
irq_local_save()

+ 이 함수를 호출하는 함수들에 대한 그래프입니다.:

void irq_local_save ( unsigned long *  value)

주어진 인자에 현재 인터럽트 상태를 저장하고 인터럽트를 비활성화 시킨다.

매개변수
[in]value저장할 변수
반환값
void 없음
참고
irq_local_restore()

+ 이 함수를 호출하는 함수들에 대한 그래프입니다.:

void isr_alignment_check_entry ( void  )

Alignment check 핸들러

void isr_bound_entry ( void  )

bound 익셉션 핸들러

void isr_breakpoint_entry ( void  )

Break point 익셉션 핸들러

void isr_coprocessor_entry ( void  )

코프로세서 핸들러

void isr_debug_entry ( void  )

디버그 익셉션 핸들러

void isr_device_not_avail_entry ( void  )

장치가 없는 경우의 익셉션 핸들러

void isr_divide_error_entry ( void  )

나눗셈 익셉션 핸들러

void isr_double_fault_entry ( void  )

더블폴트 핸들러

void isr_dummy_entry ( void  )

더미 핸들러

void isr_fdc_entry ( void  )

FDC 핸들러

void isr_general_protection_entry ( void  )

보호 에러 핸들러

void isr_hdc_entry ( void  )

HDC 핸들러

void isr_invalid_opcode_entry ( void  )

잘못된 opcode 익셉션 핸들러

void isr_invalid_tss_entry ( void  )

유효하지 않은 TSS 핸들러

void isr_irq2_entry ( void  )

IRQ2 핸들러

void isr_irq_binder_entry ( void  )

IRQ Binder 핸들러

void isr_keyboard_entry ( void  )

키보드 핸들러

void isr_machine_check_entry ( void  )

Machine check 핸들러

void isr_math_fault_entry ( void  )

math fault 핸들러

void isr_nmi_interrupt_entry ( void  )

Non-maskable-interrupt 인터럽트 핸들러

void isr_overflow_entry ( void  )

오버플로우 익셉션 핸들러

void isr_page_fault_entry ( void  )

페이지 폴트 핸들러

void isr_rtc_entry ( void  )

RTC 핸들러

void isr_segment_not_present_entry ( void  )

세그먼트가 없는 경우에 대한 핸들러

void isr_simd_floating_entry ( void  )

SIMD Floating 핸들러

void isr_stack_segment_fault_entry ( void  )

스택 세그먼트 폴트 핸들러

void isr_sw_entry ( void  )

소프트웨어 인터럽트 핸들러

void isr_timer_entry ( void  )

타이머 핸들러

int register_irq ( int  idx,
int  priority,
int(*)(void *)  handler,
void *  data 
)

인터럽트 핸들러를 등록한다.

매개변수
[in]idx인터럽트 번호
[in]priority인터럽트 처리 우선 순위
[in]handler핸들러 포인터
[in]data인터럽트 핸들러에 전달할 인자
반환값
int 성공시 0, 실패시 에러코드

Insertion sorting

+ 이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:

+ 이 함수를 호출하는 함수들에 대한 그래프입니다.:

int unregister_irq ( int  idx,
int(*)(void *)  handler,
void *  data 
)

등록된 인터럽트 핸들러를 제거한다.

매개변수
[in]idx인터럽트 번호
[in]handler핸들러 포인터
[in]data인터럽트 핸들러에 전달되던 인자
반환값
int 성공시 0, 실패시 에러코드

+ 이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:

+ 이 함수를 호출하는 함수들에 대한 그래프입니다.:

변수 문서화

struct error_code __PACKED
unsigned long kernel_cs

커널 코드 세그먼트 디스크립터 offset

unsigned long kernel_ds

커널 데이터 세그먼트 디스크립터 offset

unsigned long kernel_ss

커널 스택 세그먼트 디스크립터 offset