Linux内核-中断-中断向量表和中断请求队列的初始化

一、中断

中断通常被定义为一个事件,该事件改变处理器执行的指令顺序。中断有两种,一种是由CPU外部产生的,对于执行中的软件来说,这种中断的发生完全是“异步”的,根本无法预料此类中断会在什么时候发生,一般由其他硬件设备产生(例如键盘中断);另一种是由CPU本身在执行程序的过程中产生的,例如X86中的“INT n”。Intel手册中分别将这两种中断称为中断和异常。Intel文档中,中断又可分为可屏蔽中断和非屏蔽中断,异常分为:故障、陷阱、异常中止和编程异常。不管是哪种中断,CPU的相应过程基本上一致,即:在执行完当前指令以后,或者在执行当前指令中途,根据中断源提供的“中断向量”,在内存中找到相应的服务程序入口并调用该服务程序。外部中断的向量是由软件或硬件设置好了的,陷阱的向量是在“自陷”指令中发出的(INT n中的n),而各种异常的向量则是CPU的硬件结构中预先规定好的。系统调用一般是通过INT指令实现的,所以也与中断密切相关。

Intel X86 CPU支持256个不同的中断向量,早期X86 CPU的中断响应机制是非常简单的,内存中从0开始的1K字节作为一个中断向量表,表中每个表项占四个字节,由两字节的段地址和两字节的位移组成,构成的地址便是相应中断服务程序的入口地址。由于中断发生时,有可能涉及用户态和内核态的切换,那种简单的表项无法实现这种运行模式的切换,所以在Intel实现保护模式时,中断向量表中的表项从单纯的入口地址改成了更为复杂的描述项,称为“门”,当中断发生时必须通过这些门,才能进入相应的服务程序。按不同的用途和目的,CPU中一共有四种门:任务门、中断门、陷阱门和系统门(除了系统门外,其它三种都是用户态进程无法访问的中断门)。其中除任务门外其它三种门的结构基本相同,先看任务门,其大小为64位,结构如下图:

这里写图片描述

当中断发生时,CPU在中断向量表中找到相应表项,如果是一个任务门,CPU就会将当前任务的运行现场保存在相应的TSS中,并将任务门所指向的TSS作为当前任务,将其内存装入CPU中的各个寄存器,从而完成一次任务切换;

其它三种门大小也是64位,如下图:

这里写图片描述

三种门之间的区别为3位的类型码。与任务门相比,不同之处主要在于:任务门中不需要段内位移,因为任务门指向一个段;而其它门则指向一个子程序,所以必须结合使用段选择码和段内位移。

前面讲过,内核通过门描述符来实现运行模式的切换,这是怎么实现的呢?答案就在描述符的DPL字段,CPU先根据中断向量找到一扇门描述项,然后,将这个门的DPL与CPU的CPL相比,CPL必须小于或等于DPL,也就是优先级别不低于DPL,才能穿过这扇门。不过,如果中断是由外部产生或是因CPU异常而产生的话,就免去这一层检验。所以通过将系统门的DPL设为3,而其它的门的DPL设为0,就可以使用户态进程只能访问系统门,从而防止用户通过int指令模拟非法的中断和异常。

进入中断服务程序时,如果中断服务程序的运行级别,也就是目标代码段的DPL,与中断发生时的CPL不同,那就要引起堆栈的更换。Linux中是这样更换堆栈的:如果进程thread_union结构大小为8KB,则当前进程的内核栈被用于所有类型的内核控制路径;相反,如果thread_union结构大小为4KB,内核使用三种类型的内核栈:

  • 异常栈:用于处理异常(包括系统调用)。这个栈包括在每个进程的thread_union数据结构中。
  • 硬中断请求栈:用于处理中断。系统中每个CPU都有一个硬中断请求栈,而且每个栈占用一个单独的页框。
  • 软中断请求栈:用于处理可延迟函数(软中断或tasklet)。系统中每个CPU都有一个软中断请求栈,而且每个栈占用一个单独的页框。

二、中断向量表的初始化

Linux内核在初始化阶段完成了页式虚存管理的初始化以后,便调用trap_init()和init_IRQ两个函数进行中断机制的初始化。其中trap_init()中主要是对一些系统保留的中断向量的初始化,而init_IRQ()则主要是用于外设的中断,这两个函数源码如下:

  1. trap_init()是在arch/i386/kelnel/traps.c中定义的:

        void __init trap_init(void)
        {
        #ifdef CONFIG_EISA
            void __iomem *p = ioremap(0x0FFFD9, 4);
            if (readl(p) == 'E'+('I'<<8)+('S'<<16)+('A'<<24)) {
                EISA_bus = 1;
            }
            iounmap(p);
        #endif
    
        /*
         * APIC为高级可编程中断控制器
         */
        #ifdef CONFIG_X86_LOCAL_APIC
            init_apic_mappings();
        #endif
    
            /*
             * 程序中先设置中断向量表开头的19个陷阱门,这些中断向量都是CPU保留用于
             * 异常处理的,例如中断向量14就是为页面异常保留的
             */
            set_trap_gate(0,&divide_error);
            set_intr_gate(1,&debug);
            set_intr_gate(2,&nmi);
            set_system_intr_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_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);
            set_trap_gate(9,&coprocessor_segment_overrun);
            set_trap_gate(10,&invalid_TSS);
            set_trap_gate(
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值