系统初始化的时候,定义和初始化了中断向量表。并初始化8259的工作方式。
// 加载idt的基地址和限长到idt寄存器
lidt idt_descr
// 定义
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long _id
// 初始化
_idt: .fill 256,8,0
所以idt的内容是一个单位是8字节,长度是256的数组。linux0.11分为中断、系统、陷阱门。系统在启动的时候设置idt。
void trap_init(void)
{
int i;
set_trap_gate(0,÷_error);
set_trap_gate(1,&debug);
set_trap_gate(2,&nmi);
set_system_gate(3,&int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
// 缺页和写保护异常处理函数
set_trap_gate(14,&page_fault);
set_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error);
for (i=17;i<48;i++)
set_trap_gate(i,&reserved);
set_trap_gate(45,&irq13);
// 允许8259接收中断
outb_p(inb_p(0x21)&0xfb,0x21);
outb(inb_p(0xA1)&0xdf,0xA1);
set_trap_gate(39,¶llel_interrupt);
}
#define _set_gate(gate_addr,type,dpl,addr) \
// 把dx即处理函数地址的低16位赋值给ax,不影响eax的高16位
__asm__ ("movw %%dx,%%ax\n\t" \
/*
设置idt描述符的第三个字节的内容,左移位数是字段对应的位置偏移,
相加后赋值给dx,共16位,edx的高16位保存了处理函数的高16位地址
*/
"movw %0,%%dx\n\t" \
// 把eax即0x000080000 + 处理函数的地址赋值给idt描述符的前两个字节
"movl %%eax,%1\n\t" \
// 把edx的内容写入idt描述符的第5-8个字节
"movl %%edx,%2" \
: \
: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
// o表示使用内存地址并可以加偏移量
"o" (*((char *) (gate_addr))), \
// 指向保存选择子的首地址
"o" (*(4+(char *) (gate_addr))), \
// edx等于addr,eax等于0x00080000
"d" ((char *) (addr)),"a" (0x00080000))
#define set_intr_gate(n,addr) \
_set_gate(&idt[n],14,0,addr)
#define set_trap_gate(n,addr) \
_set_gate(&idt[n],15,0,addr)
#define set_system_gate(n,addr) \
_set_gate(&idt[n],15,3,addr)
其中idt描述符格式是,这个版本貌似还没有任务门。
在这里插入图片描述 还有一些是键盘,软盘等也设置中断。下面看一个异常处理程度。就是缺页或写保护异常的时候触发的
.globl _page_fault
_page_fault:
// 交换两个寄存器的值,esp指向的位置保存了错误码
xchgl %eax,(%esp)
// 压栈寄存器
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
// 内核数据段描述符
movl $0x10,%edx
mov %dx,%ds
mov %dx,%es
mov %dx,%fs
// 如果是缺页异常,cr2保存了引起缺页的线性地址
movl %cr2,%edx
// 线性地址(有的话)和错误码入参
pushl %edx
pushl %eax
// 1和eax与,结果放到ZF中
testl $1,%eax
// zf=0则跳转,即0是写异常,1是缺页异常
jne 1f
call _do_no_page
jmp 2f
1: call _do_wp_page
// 出栈,返回中断,会重新异常指令
2: addl $8,%esp
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
缺页或写保护异常的时候,系统会把错误码和线性地址告诉处理程序。具体的处理可以见内存管理分析那篇文件。