您好、欢迎来到现金彩票网!
当前位置:彩之网 > 中断机制 >

Linux中断机制:硬件处理初始化和中断处理

发布时间:2019-07-10 04:58 来源:未知 编辑:admin

  中断让外设能够通知CPU他需要获得服务(让CPU执行指定的中断服务例程ISR)。为了达到这个目的,首先要为中断执行做好准备,完成初始化相关的操作。包括:

  2、 配置并使能外部设备(比如使用pci_enable_msix),得到irq号;在这个操作过程中,内核需要完成的大致操作是:

  1、 确定该中断的执行CPU,并在对应CPU上建立vector和irq号的对应关系(利用全局per-cpu变量vector_irq),配置中断控制器(I/OAPIC、PIR等),可能还需要设置外部设备(比如设置MSI

  2、 中断控制器从外设获取中断电信号或者中断消息,把它翻译为vector(CPU使用这个参数来决定是谁发生了中断,要如何处理)并提交到CPU。

  在linux中可以通过/proc/interrupts查看当前系统中所有中断的统计信息,在/proc/irq/xxx(中断号)下面,可以看到该中断的详细信息。

  中断控制器的功能是:把外设的中断信号,转换成CPU能够明白的vector,并完成中断执行控制,确保在合适的时机把中断提交给CPU执行。对这部分内容,《interrupt in linux》有详细的描述。

  每个8259A有8个管脚,每个管脚对应其连接的CPU的IDT中的一个vector,单独使用8259A,其硬件连线就决定了对设备vector的使用。典型的场景是使用两个8259A级联,理论最多16个中断号(就是ISA IRQs),实际能提供对15个中断线的处理(master的IRQ2用于连接slave),其具体的分配见下图。

  用于完成输入的信号到输出信号的映射。在下图中PIR被用于完成多个PCI设备的INT#信号到8259A对应引脚的路由。对应这种连接方式,在PCI设备初始化的时候,OS会根据BISO提供的信息设置PIR,把INT#路由到O0-O3中正确的管脚,从而体现到8259A的正确管脚(对应了vector),这样INT#信号就被转换为vector并提交到CPU。由于可能有较多的PCI设备,而PIR的输入/出错管脚有限,所以连接到相同输入关键的INT#会共享一个中断。

  每个I/O APIC提供24个管脚,能够和外部设备的中断线连接,每个管脚都可以通过配RTE(Redirection table entry)配置对应的vector。其功能是:把外部设备的中断请求,翻译为local APIC的interrupt message,并按照配置的vector,发送给指定的local APIC处理(在SMP系统,存在多个CPU,也就有多个local APIC)。通常的配置方式是:第一个I/O APIC的前16个管脚,配置来处理之前的ISA IRQs,其它外设比如PCI设备,则直接使用其他管脚连接。

  每个local APIC对应了一个CPU。其处理interrupt message的过程如下:1、 判断该中断的destination是否为当前APIC,如果不是则忽略,否则继续处理2、 如果是SMI/NMI/INIT/ExtINT, or SIPI(这些中断都负责特殊的系统管理任务,外设一般不会使用)被直接送到CPU执行,否则执行下一步。

  4、 如果该中断优先级高于当前CPU正在执行的中断,且当前CPU没有屏蔽中断(按照X86和LINUX的实现,这时是屏蔽了中断的),则该高优先级中断会中断当前正在执行的中断(置ISR位,并开始执行),低优先级中断会在高优先级中断完成后继续执行,否则只有等到当前中断执行完成(写了EOI寄存器)后才能开始执行下一个中断。

  5、 在CPU可以处理下一个中断的时候,从IRR中选取最高优先级的中断,清0 IRR中的对应位,并设置ISR中的对应位,然后ISR中最高优先级的中断被发送到CPU执行(如果其它优先级和屏蔽检查通过)。

  6、 CPU执行中断处理例程,在合适的时机(在IRET指令前)通过写EOI寄存器来确认中断处理已经完成,写EOI寄存器会导致local APIC清理ISR的对应bit,对于level trigged中断,还会向所有的I/O APIC发送EOI message,通告中断处理已经完成。

  对应通过local APIC发送到CPU的中断,按照其vector进行优先级排序:

  数值越大,优先级越高。由于local APIC允许的vector范围为[16,255],而X86系统预留了[0,31]作为系统保留使用的vector,实际的用户定义中断的优先级的取值范围为[2,15],在每个优先级内部,vector的值越大,优先级越高。

  Local APIC中还有一个关于中断优先级的寄存器TPR(task priority register)寄存器:用于确定打断线程执行需要的中断优先级级别,只有优先级高于设置值的中断才会被CPU执行 (SMI/NMI/INIT/ExtINT, or SIPI不受限制),也就是除了特殊中断外,优先级低于TPR指定值的中断将被忽略。

  对于同一个vector,如果有多次中断请求,可能IRR和ISR对应的bit位都被置位,也就是对同一个vector,local APIC可以pending两个中断,其后的即使有多处,也会被合并为一个执行。

  中断的执行总是在指令边界开始(只有一个特殊的exception:abort在外,出现了这个中断,系统基本上也就完蛋了),也就是中断不可能打断指令的执行。

  1、 vector(中断向量)vector是一个整数,在X86CPU上,使用vector对中断(interrupt,外部设备产生)和异常(exception,CPU在程序执行中产生)统一编号,每个CPU核心内部,中断/异常和vector所以一一对应的;但是在各个不同的CPU核心上,相同的vector可以对应不同的中断(至少对于linux的设置,异常还是使用相同的vector)。vector的取值范围为[0,255],其中[0,31]被系统保留使用(多数作为异常的vector),其余的可供外设中断使用(系统设备比如local APIC也占用了部分[32,255]这个范围的vector)。

  X86 CPU采用一个有256个元素的数组来描述中断/异常,该数组的index为vector;其内容包括了三种gate descriptor,用于描述一个中断/异常的处理接口;这个数组就是IDT,CPU在收到中断请求的时候,就利用vector获取到对应的中断处理接口描述并执行。

  通过CPU INTR管脚/local APIC接收到的中断是可屏蔽中断,这些中断能够通过清零EFLAGS的IF来屏蔽(CLI指令)。通过INT n指令生成的中断即使使用了和外部中断一样的vector,也是不可屏蔽的;同样CPU运行过程中同步产生的trap、fault、abort等异常也是不可屏蔽的。

  NMI是不可屏蔽中断(不可通过IF标志屏蔽),是通过CPU的NMI管脚发出的中断或者通过delivery mode为NMI的方式提交的中断。NMI中断在执行前,CPU不仅会屏蔽其它中断,也会屏蔽NMI中断,直到NMI中断处理执行完成(IRET指令被执行)。使用INT 2指令虽然能执行NMI中断处理函数,但是相关硬件不会介入,也就是没有相关的屏蔽NMI中断的操作。

  1、 利用vector,查IDT得到中断描述符;2、 如果中断发生在用户态,会首先执行stack switch切换到内核态执行;3、 依次保存EFLAGS CS IP到当前栈,如果需要(有error code的异常),把error code PUSH到当前栈。并把IF/TF位清零屏蔽可屏蔽中断;至此,CPU完成了中断处理程序执行环境的建立。

  5、 根据环境执行不同的中断退出方式,比如执行现场调度操作(retint_careful和retint_kernel),最终都会执行IRET指令;至此,中断执行完成。

  每个PCI设备用四个中断信号,对应INTA#、INTB# INTC#、INTD#,这些中断信号采用level trigger 的方式并且为低电平有效,PCI设备通过拉低对应的信号来assert对应的中断,并在ISR访问PCI设备的指定寄存器deassert该中断。

  这里存在两种常见连接模式,一种是使用老的8259A+PIR的系统,一种是使用新的I/O APIC的系统。对于使用8259A的系统:PCI的中断线连接到一个可编程的PIR设备,再通过该设备连接到8259A(见X86中断控制器一章的图);对于采用I/OAPIC的系统,可以使用以下的连接方式,同样这里只画出了一个中断线,同时根据不同的系统配置可能存在多个I/OAPIC。除了采用直接的中断引脚连接,PCI还支持virtual INT#,使用INT# message(Assert INT# message和deassert INT# message)的方式来使用INT#信号。

  1、 中断数量有限且不方便扩展:每个物理的PCI设备,最多只有4个中断但是至少能支持8个function,且系统中可能存在多个PCI设备,不得不使用中断共享的模式,影响使用性能。2、 同步问题:由于INT#中断采用的是side channel,中断信号和数据本身存在不同步的问题:可能在中断到达的时候,对应的数据没有达到,为了处理这个问题,一般采用“读刷新”的做法,也就是在使用该设备写入到X86的数据之前,ISR先对这个设备进行一次读操作来确保相关数据已经写入完成,比如读PCI设备的中断状态寄存器等。

  在这种模式下,PCI设备通过和数据DMA一样的通道来完成中断处理,通过向特定地址空间(系统FSB Interrupt存储器空间)发起一个写操作来发起中断。该写操作的地址和数据信息在PCI设备初始化MSI功能的时候已经填写到MSI Capacity registers(MSI模式)/MSI-X table(MSI-X)中(对X86,这个地址空间是FEE00000H开始的地址空间,其实就是local APIC寄存器映射的地址空间),地址信息保存在Message address register,其中包含了目标CPU信息和FSB Interrupt存储器空间;数据中包含了该MSI中断对应的vector,保存在Message data register中。 MCH(memory control hub)截获这个写操作,转换为FSB interrupt message并向各个CPU核心广播,local APIC接收并处理这个消息,最终触发CPU的中断处理过程。使用这种机制,中断的数量不受PIR/ IOAPIC等各种器件管脚数量的限制,MSI可以支持32个中断,而MSI-X可以达到2048个;中断的传递相当直接,省略了中断路由的过程;并且能直接从interrupt message中获取vector信息,减少了交互过程。

  1、 irq号:在当前系统中全局唯一,对应内核数据结构struct irq_desc,每个外设的中断有一个irq号(体系结构预留的中断,是没有对应的irq_desc结构和irq号的),该irq在该中断的生命周期内都不会改变,且和该中断的中断处理函数关联;内核使用一个bitmap allocated_irqs来标识当前系统已经分配的irq;irq号的管理与底层中断设备和配置无关,属于Generic Interrupt Layer;对于irq号分布集中的情况,不配置CONFIG_SPARSE_IRQ,内核采用数组直接管理,数组下标就是irq号;而对于irq号比较分散的,设置CONFIG_SPARSE_IRQ,内核采用radix tree来管理所有的irq号。

  2、 vector号:内核使用全局bitmap used_vectors来标识那些vector被系统预留,不能被外设分配使用。

  3、 irq号和vector号的关联:内核中使用per-cpu变量vector_irq来描述irq号和vector号的关联,对每个CPU,vector_irq是一个数组,在X86架构下成员数量为256,其数组的index为vector,值为irq,如果为-1则表示该CPU上的这个vector尚未分配。

  4、 struct irq_desc结构,用来描述一个中断,是内核generic interrupt layer的关键数据结构,其包含了中断的大部分信息,并连接了driver层和物理中断设备层,每个irq号对应一个该结构,共享相同irq号的中断共享该结构。它的关键成员包括:

  3、 设置外设 (包括ISA中断)使用的中断处理接口,这些中断处理接口都一样。

  可以看到,这个过程会完成每个中断vector对应的idt entry的初始化,系统把这些中断vector分成以下几种:1、X86保留vector,这些vector包括[0,0x1f]和APIC等系统部件占用的vector,对这些vector,会记录在bitmap used_vectors中,确保不会被外设分配使用;同时这些vector都使用各自的中断处理接口,其中断处理过程相对简单(没有generic interrupt layer的参与,CPU直接调用到各自的ISR)。

  3、其它外设的中断,对这些中断,在初始化过程中仅设置了对应的IDT,和ISA中断一样,其中断处理接口都来自interrupt数组。

  )而interrupt是一个数组,该数组在初始化完成后释放,其每个数组项都是一个地址,是对应的“pushq_cfi”代码的地址(每个代表中断入口的标号)。系统在初始化的时候,对外设使用interrupt数组作中断处理接口,就是在中断发生时,执行代码段:

  除了中断路由表,其它两种机制的初始化(包括相关中断路由信息的初始化)的在《interrupt in linux》中有很详细的描述。这些初始化操作都在内核初始化的时候完成。

  4、 这些通用处理接口会调用中断初始化的时候注册的外部中断处理函数;完成EOI等硬件相关操作;并完成中断处理的相关控制。

  a) 中断不会丢如果中断触发时中断被屏蔽,那么中断控制器会记录下该中断,在屏蔽取消的时候会再执行。b) edge触发的缺点是完成共享不方便:

  比如A和B两个中断源共享一个中断,每次ISR先检查A再检查B,如果B先发生中断,在ISR检查完A,检查B的过程中,A发生中断。那么在ISR处理开始的时候,A会告诉ISR,不是它干的,然后ISR处理B的中断,完成后通过清理中断源把B的电压归位,但是由于A的中断没有得到处理,电压没有归位,这个共享的中断就不能得到再次触发了。

  b) 对中断触发时中断被屏蔽的情况,如果中断屏蔽解除后仍然引脚电压仍然在门限值,就执行该中断的ISR,否则不执行。

  1、 在local APIC层次(当前CPU),一个中断正在处理的时候,不会有相同的中断或者优先级低于该中断的其它中断来打断当前中断的执行;但是高优先级中断可以打断低优先级中断。

  2、 在X86 CPU层次(当前CPU),从中断执行开始到IRET,IF位都被清零,也就是只有不可屏蔽中断能够打断当前中断的执行。

  3、 在Generic interrupt layer层次,如果一个中断已经在系统中执行,会阻止该中断在其它CPU上的执行。

  4、 在外设/驱动中断处理函数层次往往也有中断使能的功能,比如启用了NAPI的网卡,在中断处理函数开始执行的时候,往往会通过硬件功能关闭该中断,要在对应的软中断完成处理后才通过硬件功能使能该中断。

  注:NMI中断虽然称为不可屏蔽中断,也有一个例外:NMI中断执行过程中,该CPU屏蔽了后来的NMI中断。

  通过中断初始化过程我们知道:中断在那个CPU上执行,取决于在那个CPU上申请了vector并配置了对应的中断控制器(比如local APIC)。如果想要改变一个中断的执行CPU,必须重新申请vector并配置中断控制器。一般通过echo xxx

  /proc/irq/xxx/affinity来完成调整,同时irq_balance一类软件可以用于完成中断的均衡。

  Linux阅码场是专业的Linux及系统软件技术交流社区,Linux系统人才培养基地,企业和Linux人才的连接枢纽。

  荐:发原创得奖金,“原创奖励计划”来了!举报悬赏令!现金奖励等你拿-->

http://ando2.com/zhongduanjizhi/195.html
锟斤拷锟斤拷锟斤拷QQ微锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷微锟斤拷
关于我们|联系我们|版权声明|网站地图|
Copyright © 2002-2019 现金彩票 版权所有