// 控制单元对中断信号的处理: // 当cpu执行一条指令后,cs和eip包含下一条要执行的指令的逻辑地址,在处理那条指令之前, // 控制单元会检查在运行前一条指令时是否发生了一个中断或异常,如果发生了一个中断或异常, // 控制单元执行下列操作: // 1.确定与中断或异常关联的向量i // 2.读入由idtr寄存器指向的IDT表中的第i项 // 3.从gdtr寄存器获得GDT的基地址,并在GDT中查找,以读取IDT表项中的选择符所标识的段描述符。 // 这个描述符指定中断或异常处理程序的基地址 // 4.权限和安全性检查 // 5.检查是否发生了特权级的变化,也就是说,CPL是否不同于所选择的段描述符的DPL,如果是, // 控制单元必须开始使用与新的特权级相关的栈。 // 5.1 读入tr寄存器,以访问运行进程的TSS段 // 5.2 用与新特权级相关的栈段和栈指针的正确值装在ss和esp,这些值可以在tss中找到。 // 5.3 在新的栈中保存ss和esp以前的值,这些值定义了与旧特权级相关的栈的逻辑地址。 // 6.如果故障已发生,用引起异常的指令地址装载cs和eip寄存器,从而使得这条指令能再次被执行。 // 7.在栈中保存eflags,cs,及eip的内容。 // 8.如果异常产生了一个硬件出错码,则将其保存在栈中。 // 9.装载cs和eip寄存器,其值分别时IDT表中第i项门描述符的段选择符和偏移量字段。这些值给出了 // 中断或者异常处理程序的第一条指令地址。 // 10.由中断处理程序,执行中断处理。 // 11.中断或异常处理完毕后,相应的处理程序必须产生一条iret指令,把控制权转交给被中断的程序, // 迫使控制单元: // 11.1 用保存在栈中的值装载cs、eip或eflags寄存器。 // 11.2 检查处理程序的CPL是否等于cs中最低两位的值(这意味着被中断的进程与处理程序运行在 // 同一特权级),如果是,iret终止执行;否则,转入下一步 // 11.3 从栈中装载ss和esp寄存器,因此返回到与旧特权级相关的栈。 // 中断向量: // 1.每个中断和异常是由0~255之间的一个数来标识的,intel将这个8位的无符号整数叫做一个向量。 // 2.中断向量是Intel从IA-32 CPU角度看到的中断信号划分;中断号则是Linux系统对外部中断的号码分配。 // 当外设把中断信号递送给PIC时,与之关联的是一个“中断号”(每个中断号对应一条中断线,从软件的角度来看,这两个术语可以混用); // 当PIC把这个中断信号发送给CPU时,与之关联的是一个“中断向量”。 // 3.通常,不可屏蔽中断和异常的中断向量是固定的,而可屏蔽中断的中断向量则可以对中断控制器进行编程来改变。 // 4.i386 CPU的256个中断向量是这样分配的: // 4.1 0~31这一共32个向量用于异常和不可屏蔽中断。 // 4.2 32~47这一共16个向量用于可屏蔽中断,分别对应于主、从8259A中断控制器的IRQ输入线 // 4.3 剩余的48~255用于标识软中断 // 5.Linux全部使用了0~47之间的向量。但对于48-255之间的软中断向量,Linux只使用了其中的一个,即用于实现系统调用的中断向量128(0x80)。 // 当用户态下的进程执行一条int 0x80汇编指令时,CPU切换到内核态,以服务于系统调用。 // i386的IDT // i386 CPU的IDT表一共有256项,分别对应每一个中断向量。每一个表项就是一个中断描述符,用以描述相对应的中断向量,中断向量就是该描述符 // 在IDT中的索引,每一个中断描述符的大小都是8个字节。根据INTEL的术语,中断描述符也称为“门(Gate)”。 // IDT表 // idt_table首地址保存在idt寄存器中 1.1 struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, }; // CPU异常初始化 // 调用路径: start_kernel->trap_init // 函数主要任务: // 1.初始化apic控制器 // 2.注册0~19号异常、中断的处理函数,128号系统调用处理程序 // 3.注册系统调用的处理函数 // 4.cpu的初始化 // 内核源码:arch/x86/kernel/traps.c // arch/x86/kernel/entry_32.S 1.2 void __init trap_init(void) { //cpu内部apic控制器 init_apic_mappings(); //初始化中断、异常、陷阱门描述符 set_trap_gate(0,÷_error); set_intr_gate(1,&debug); //不可屏蔽中断 set_intr_gate(2,&nmi); //系统中断门 set_system_intr_gate(3, &int3); //系统门 set_system_gate(4,&overflow); set_system_gate(5,&bounds); //陷阱门 set_trap_gate(6,&invalid_op); set_trap_gate(7,&device_not_available); set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS); 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_intr_gate(14,&page_fault); set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); set_trap_gate(19,&simd_coprocessor_error); //系统调用描述符 set_system_gate(SYSCALL_VECTOR,&system_call); //cpu相关的初始化 cpu_init(); } // set_XXX_gate的设置异常处理函数地址 // intr_gate、trap_gate区别在于DPL不同 // 设置中断门描述符 // 函数主要任务: // 在IDT表的第n个表项插入一个中断门 // 1.1 门中的段选择子设置为内核代码段的段选择子 // 1.2 偏移量设置为中断处理程序的地址addr // 1.3 DPL字段设置为0 1.3 void set_intr_gate(unsigned int n, void *addr) { _set_gate(idt_table+n,14,0,addr,__KERNEL_CS); } // 第一个外设中断(即8259A的IRQ0)所对应的中断向量 #define FIRST_EXTERNAL_VECTOR 0x20 // 系统调用的中断向量 #define SYSCALL_VECTOR 0x80 // 中断初始化 // 调用路径:start_kernel->init_IRQ // 函数主要任务: // 1.初始化中断描述符的中断控制器 // 2.初始化20~255号中断的入口处理函数 // 3.添加体系结构特定的门 // 4.初始化时钟中断 // 5.初始化当前cpu的中断栈 2.1 void __init init_IRQ(void) { int i; //建立中断描述符与中断控制器的联系 pre_intr_init_hook(); //设置20~255号中断的入口函数 for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) { //从第一个外部中断开始 int vector = FIRST_EXTERNAL_VECTOR + i; if (i >= NR_IRQS) break; //中断门描述符 if (vector != SYSCALL_VECTOR) set_intr_gate(vector, interrupt[i]); } //添加体系结构特定的门 intr_init_hook(); //初始化时钟中断 setup_pit_timer(); //初始化当前cpu的中断栈 irq_ctx_init(smp_processor_id()); } // 中断描述符(IRQn描述符) // i8259a使用0~15号IRQ 2.2 struct irqdesc irq_desc[NR_IRQS]; // 中断描述符初始化 // 函数主要任务: // 1.禁用所有IRQ // 2.0~15号IRQ关联i8259a中断控制器 // 注: // 中断关闭嵌套,当depth=0时,中断开启。 2.3 void __init init_ISA_irqs (void) { int i; //初始化cpu的apic init_bsp_APIC(); //初始化8259a中断控制器 init_8259A(0); //8259使用的中断号(IRQn) for (i = 0; i < NR_IRQS; i++) { //初始化时,所有irq关闭 irq_desc[i].status = IRQ_DISABLED; //没有驱动注册中断处理例程 irq_desc[i].action = NULL; //嵌套深度为1的关闭状态 irq_desc[i].depth = 1; //0~16号irq,关联i8259a中断控制器 if (i < 16) { irq_desc[i].handler = &i8259A_irq_type; } else { // >16的pci中断,按需分配 irq_desc[i].handler = &no_irq_type; } } }
时间: 2024-09-20 09:31:37