Welcome to My World (www.dgmayor.com)

소프트웨어/C & 자료구조 & 커널 & DB

11. 인터럽트 - IDT 선언

dgmayor 2022. 6. 14. 11:19
728x90
 

10~13강 소스코드입니다.
 

--------------------------------------------------------------------------------
 
 
 
저번에 설명했듯이 인터럽트를 구현하기 위해서는 크게 3단계를 구현해야 합니다.
 
1. PIC 셋팅 ( 하드웨어 셋팅 )
2. IDT ( 전에 구현했던 GDT와 같은 종류 )
3. ISR ( 실제 인터럽트 발생 시 실행되는 코드 )
 
요번 포스트에서는 2. IDT ( 전에 구현했던 GDT와 같은 종류 ) 을 다뤄보도록 하죠. 구글링을 통한 여러 포스트를 살펴보면 IDT를 어셈블리로 구현하는 분들이 대부분입니다. 물론 그렇게 하면 나름 쉽게 셋팅이 가능합니다. 하지만 저는 C언어를 통해 IDT와 ISR을 구현해보도록 하겠습니다. 이렇게 해야 훗날에 개발하기가 훨씬 편해지기 때문입니다. ( 과연..? )
 
 
--------------------------------------------------------------------------------
 
 
IDT를 구현하는 방식은 GDT를 구현했던 방식과 완전히 똑같습니다. 각 인터럽트에 해당하는 정보들을 듬뿍 담아주고 이를 lidt라는 특정 레지스터에 등록해주기만 하면 됩니다.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
lgdt[gdtr]
    
mov eax, cr0
or eax, 1
mov cr0, eax
    
jmp $+2
nop
nop
 
gdtr:
dw gdt_end - gdt - 1
dd gdt+0x7C00
 
gdt:
 
    dd 0,0 ; NULL 세그
    CodeSegment equ 0x08
    dd 0x0000FFFF0x00CF9A00 ; 코드 세그
    DataSegment equ 0x10
    dd 0x0000FFFF0x00CF9200 ; 데이터 세그
    VideoSegment equ 0x18
    dd 0x8000FFFF0x0040920B ; 비디오 세그
 
gdt_end:
 
 
cs
 
 
예전에 구현했던 GDT 테이블 코드를 참고해봅시다. 똑같이 gdt: ~ gdt_end: 부분을 만들어주고, gdtr: 같은 곳에다 크기와 주소를 각각 저장해놓은 후 이를 lgdt가 하는 것처럼 등록해놓으면 끝입니다. GDT를 등록하는 원리와 IDT를 등록하는 원리가 완벽하게 같다는 것입니다!
 
단지 차이점이 있다면 다음과 같이 2가지가 있겠습니다.
 
1. GDT와 다르게 IDT의 각 오프셋은 실제 인터럽트 발생시 실행될 주소를 가지고 있어야 한다.
인터럽트가 발생하게 되면 CPU는 IDT를 참고하게 되고 저장되어 있는 주소로 이동합니다. 그 주소에는 실제 인터럽트 발생시 실행하고 싶은 명령어들이 있어야 합니다. 우리는 이를 함수로 만들어놔서 해당 IDT 테이블들이 이 함수를 가리키게 만들겁니다. 그리고 각각의 함수에 코드를 작성해놓으면 인터럽트 발생시 이 함수로 이동해서 함수를 실행하겠죠?
 
2. IDT는 물리주소 0x0번지에 위치해야 한다.
그렇기 때문에 gdtr: 의 두 번째 값에 해당하는 dd 값은 무조건 0이 되야 합니다. 이를 구현하기 위해서는 일단 IDT를 어디에 만들어놓은 후, 일괄적으로 0x0번지에다 복붙해야 하는 일이 필요할 겁니다.
 
3. IDT 크기는 256개다.
엄밀하게 반드시 256개를 구현해야 하는지는 모르겠지만, 아무튼 저는 256개를 다 구현할 겁니다. 이걸 언제 다 구현하고, 또 각각의 IDT가 가리키는 함수를 언제 다 구현합니까? 분명 귀찮은 일입니다. 이 노가다를 피하기 위해서 idt_ignore 즉 무시해도 되는 함수 하나를 만들어놓고 256개 몽땅 이 함수를 가리키게 만들겁니다. 그리고 우리가 정말 필요로 하는 인터럽트인 타이머와 키보드 각각의 함수를 만들어 놓은 후, 해당 IDT 테이블만 이를 가리키도록 만들면 됩니다!
 
보다 구체적인 설명은 아래의 링크를 확인하시기 바랍니다.
 
 

--------------------------------------------------------------------------------
 
 

아주 좋습니다. 이제 우린 이를 C언어로 구현하려고 합니다. 그렇다면... gdt: ~ gdt_end: 같이 반복되는 타입을 저장하는 것을 C에선 어떻게 구현할 수 있을까요...? 바로 구조체 아니겠습니까??
 
당장 interrupt.h 파일을 하나 만들고 다음과 같이 적어봅시다.
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma once
 
void init_intdesc();
void idt_ignore();
void idt_timer();
void idt_keyboard();
 
struct IDT
{
    unsigned short offsetl;
    unsigned short selector;
    unsigned short type;
    unsigned short offseth;
 
}__attribute__((packed));
 
struct IDTR
{
    unsigned short size;
    unsigned int addr;
 
}__attribute__((packed));
 
 
cs
 

아름다운 코드(?) 입니다. IDT에는 offset_low, selector, type, offset_high 같이 IDT 스펙을 그대로 따르는 부분을 정의했습니다. 여기서 short는 2바이트, 즉 16비트를 저장할 수 있는 자료형이죠? 그런데 그냥 short라고 선언하면 음수가 저장되서 혹시 엉뚱한 값이 나올까 unsigned를 붙였습니다.
 
IDTR은 전체 IDT 테이블의 갯수, 그리고 주소를 저장할 수 있습니다. int형은 4바이트, 32비트 저장 가능합니다. 여기서 눈에 띄는 건 __attribute__((packed)) 이라는 선언입니다. 원래 구조체 선언할 때 컴파일러가 프로그래머의 의도와 다르게 데이터 중간중간에 padding 이라는 이상한 자료형을 집어넣습니다. 근데 우리는 지금 그런 불순물(?)이 하나라도 들어가면 실행이 제대로 되지 않는 구조죠? 이를 방지하는 코드를 집어넣은 겁니다.
 
함수를 보면 어떤가요? 위에서 설명한 init_ignore()는 말 그대로 무시할 수 있는 인터럽트들이 가리키는 함수입니다. 해당 함수 안에서는 아무 것도 하지 않으면 딱이겠군요. 타이머와 키보드도 마찬가지로 함수를 만들어놓읍시다. 그리고 init_intdesc()를 만들어서 그 안에서 모든 인터럽트 관련된 코드를 진행해봅시다.
 
자 이렇게 만들어놓고 main.c에다 헤더와 함수 호출, 이렇게 두 줄 추가해주면 됩니다.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
// main.c
 
#include "function.h"
#include "interrupt.h"
 
void main()
{
    kprintf("We are now in C!"1010);
    
    init_intdesc();
 
}
 
cs
 

좋습니다! 이제 남은 것은 init_intdesc()를 구현해서 IDT와 ISR을 등록하는 것입니다.
 
다음 강의에서 구현해보겠습니다.
 
감사합니다
728x90

'소프트웨어 > C & 자료구조 & 커널 & DB' 카테고리의 다른 글

13. 인터럽트 - ISR 구현  (0) 2022.06.14
12. 인터럽트 IDT 구현  (0) 2022.06.14
10. 인터럽트 - PLC 세팅  (0) 2022.06.14
9. C언어로 OS 만들기  (0) 2022.06.13
8. 커널 프로그램 만들기  (0) 2022.06.13