|
ncloader
0.1
|
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_regs * | do_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.
인터럽트 관련 아키텍쳐 독립적 인터페이스 정의
x86 에서는 인터럽트에 대한 설명과 함께 관련 인터럽트 서비스 루틴의 시작 주소를 인터럽트 디스크립터 테이블을 통해 관리한다. 시스템 소프트웨어 개발자는 인터럽트 디스크립터 테이블을 구성한 후 CPU 에게 알려주기면 하면 되는 것이다. 인터럽트 디스크립터 테이블의 각 엔트리는 아래와 같다.
이 테이블을 CPU 에게 알려주기 위해서는 lidt 라는 명령을 사용한다. Load Interrupt Descriptor Table 의 약자이다. 여기서 어떤 독자는 혼란 스러울 수 있는데, CPU 에게 알려주는 것이면 Store 하는 것이 아니냐 이다. 하지만 이 명령은 CPU 의 입장에서 바라보는 것이므로 Load 라고 하는 것이다. 반대로 sidt 라는 명령은 현재 CPU 가 가지고 있는 IDT 정보를 읽어 오는 것이다.
우리가 추가해 주어야 하는 인터럽트 목록이다.
하지만 뭔가 아쉬운 것이 있다. IDT 를 아무리 뒤져봐도 Timer interrupt handler 가 없다는 것이다. 그럼 이 Interrupt handler 와 관련된 정보는 어디서 찾아 볼 수 있을까?
그것이 바로 PIC 라고 하는 것과 관련된 것이다.
8259 Programmable Interrupt Controller
PIC 를 초기화 하는 과정에서 Interrupt 발생 번호를 조정했었다. 앞에 설명된 기본 IDT 테이블 이후의 번호에 PIC 와 관련된 장치들로 부터 발생하는 인터럽트들이 호출될 수 있도록 재조정 했다는 것이다.
이 인터럽트들은 값이 조정이 되어 0x20 부터 IRQ0 가 remapping 된다. PIC 와 관련된 자세한 설명은 8259 Programmable Interrupt Controller 페이지를 참조하기 바란다.
이렇게 H/W 로 부터 발생하는 Interrupt 와 CPU 로 부터 발생하는 Interrupt 의 차이로는, Error Code 가 있다.
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 를 일관되게 처리하기 위해서이다.
이렇게 Stack 의 모양이 같아 지도록 맞춘 후에, 한 가지 더 해야 할 것이 있다. 그것은 다음장의 "CPU Context 관리" 에서 확인해보기로 한다.
아래 두 그림은 특권레벨(RING)의 변화가 있을 때와 없을 때의 스택의 변화를 보여주고 있다.
Stack 의 변화가 Ring 에 따라서 달라지지만, 앞서 말한 것 처럼, CPU 인지 H/W 인지에 따라서도 달라진다. 일단 Interrupt Service Routine 이 들어오면, 한가지 더 해줘야 할 것이 있다. 그것이 바로 CPU Context 를 유지하는 일이다.
이렇게 구현한 Service Router 를 살펴 보면, Segement Selector 와 각종 Register 들을 Stack 에 Push 하는 것을 볼 수 있다. 이렇게 하는 이유는 Interrupt Service 를 마치고 원래 하던 루틴으로 복귀 할 때, Service Routine 을 수행 하면서 CPU Register 들이 변경되는 경우, 원래 루틴으로 복귀 한 후에 Register 에 저장했던 값들이 바뀌는 문제를 해결하기 위해서 이다.
인터럽트 디스크립터를 모두 채워 넣었다면, TSS 설정도 확인해야 한다. 부트로더에서 User level 과 Kernel level 간의 전환은 없을 예정이지만, NCKernel 에서는 사용할 예정이므로 간략히 확인하고 넘어가자.
컨텍스트가 바뀔 때, EFLAGS 도 다시 설정된다. EFLAGS 에 Interrupt 와 관련된 Bit flag 가 있다.
인터럽트 핸들러를 등록할 때, 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 들이 실행되는 순서가 정해지며, 가장 낮은 값이 가장 먼저 실행된다.
| #define asmlinkage __attribute__((regparm(0))) |
| #define HIGH_PRIORITY 0x80000000 |
IRQ 등록시 가장 높은 우선 순위로, 가장 먼저 실행 된다.
| #define LOW_PRIORITY 0x7FFFFFFF |
IRQ 등록시 가장 낮은 우선 순위로, 가장 나중에 실행 된다.
| #define NORMAL_PRIORITY 0 |
IRQ 등록시 일반 적인 우선 순위를 정의한다.
| enum gate_size |
| enum idt_type |
| enum irq_nr |
| void disable_interrupt | ( | void | ) |
인터럽트를 비활성화 시킨다.
이 함수를 호출하는 함수들에 대한 그래프입니다.:| asmlinkage struct pt_regs* do_irq | ( | struct pt_regs * | ret) |
인터럽트 요청을 처리하는 함수
| [in] | ret | 인터럽트 요청이 발생했을 때 CPU 의 Context |
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:| void enable_interrupt | ( | void | ) |
인터럽트를 활성화 시킨다.
이 함수를 호출하는 함수들에 대한 그래프입니다.:| void irq_local_restore | ( | unsigned long | value) |
주어진 인자의 값으로 인터럽트 상태를 복원한다.
| [in] | value | 저장된 인터럽트 상태값. |
이 함수를 호출하는 함수들에 대한 그래프입니다.:| void irq_local_save | ( | unsigned long * | value) |
주어진 인자에 현재 인터럽트 상태를 저장하고 인터럽트를 비활성화 시킨다.
| [in] | value | 저장할 변수 |
이 함수를 호출하는 함수들에 대한 그래프입니다.:| 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 | 인터럽트 핸들러에 전달할 인자 |
Insertion sorting
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:| int unregister_irq | ( | int | idx, |
| int(*)(void *) | handler, | ||
| void * | data | ||
| ) |
등록된 인터럽트 핸들러를 제거한다.
| [in] | idx | 인터럽트 번호 |
| [in] | handler | 핸들러 포인터 |
| [in] | data | 인터럽트 핸들러에 전달되던 인자 |
이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:| struct error_code __PACKED |
| unsigned long kernel_cs |
커널 코드 세그먼트 디스크립터 offset
| unsigned long kernel_ds |
커널 데이터 세그먼트 디스크립터 offset
| unsigned long kernel_ss |
커널 스택 세그먼트 디스크립터 offset