精华内容
下载资源
问答
  • 一个完整的中断处理过程应该包括
    千次阅读
    2020-12-23 01:30:11

    一个完整的中断处理过程应该包括:中断请求、

    中断排队或中断判优、中断响应、中断处

    理和中断返回等环节,下面分别进行讨论。

    1

    .中断请求

    中断请求是由中断源向

    CPU

    发出中断请求信号。外部设备发出中断请求信号要具备以

    下两个条件:

    (

    1

    )外部设备的工作已经告一段落。例如输入设备只有在启动后,将要输入的数据送

    到接口电路的数据寄存器(即准备好要输入的数据)之后,才可以向

    CPU

    发出中断请求。

    (

    2

    )系统允许该外设发出中断请求。如果系统不允许该外设发出中断请求,可以将这

    个外设的请求屏蔽。

    当这个外设中断请求被屏蔽,

    虽然这个外设准备工作已经完成,

    也不能

    发出中断请求。

    2

    .中断排队

    中民申请是随机的,有时会出现多个中断源同时提出中断申请。但

    CPU

    每次只能响应

    一断源的请求,

    那么究竟先响应哪一个中断源的请求呢?这就必须根据各中断源工作性质的

    轻重缓急,

    预先安排一个优先级顺序,

    当多个中断源同时申请中断时,

    即按此优先级顺序进

    行排队,等候

    CPU

    处理。一般是把最紧迫和速度最高的设备排在最优先的位置上。

    CPU

    先响应优先级别最高的中断源。当中断处理完毕,再响应级别低的中断申请。

    中断排队可以采用硬件的方法,

    也可以采用软件的方法。

    前者速度快,

    但需要增加硬设

    备;后者无需增加硬设备,但速度慢,特别是中断源很多时尤为突出。

    软件优用查询技术。当

    CPU

    响应中断后,就用软件查询以确定是哪些外设申请中断,

    并判断它们的优先权。一个典型的软件优先权排队接口电路如

    6

    10

    所示,图中把

    8

    外设的中断请求触发器组合起来,

    作为一具端口,

    并赋以设备号。

    把各个外设的中断请求信

    号相

    后,作为

    INTR

    信号,故其中任一外设有中断请求,都可向

    CPU

    送出

    INTR

    信号。

    CPU

    响应中断后,把中断寄存器的状态作为一个外设读入

    CPU

    ,逐位检测它们的状态,

    若哪一位为

    1

    ,则该位对应的外设有中断请求,应转到相应的服务程序的入口。其流程如图

    6

    11

    所示。

    更多相关内容
  • 中断和中断处理流程

    千次阅读 2020-12-20 04:41:20
    1. 中断概念中断是指由于接收到来自外围硬件(相对于中央处理器和内存)的异步信号或来自软件的同步信号,而进行相应的硬件/软件处理。发出这样的信号称为进行中断请求(interrupt request,IRQ)。硬件中断导致处理器...

    1. 中断概念

    中断是指由于接收到来自外围硬件(相对于中央处理器和内存)的异步信号或来自软件的同步信号,而进行相应的硬件/软件处理。发出这样的信号称为进行中断请求(interrupt request,IRQ)。硬件中断导致处理器通过一个上下文切换(context switch)来保存执行状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为CPU指令集中的一个指令,以可编程的方式直接指示这种上下文切换,并将处理导向一段中断处理代码。中断在计算机多任务处理,尤其是实时系统中尤为有用。这样的系统,包括运行于其上的操作系统,也被称为“中断驱动的”(interrupt-driven)。

    中断是一种使CPU中止正在执行的程序而转去处理特殊事件的操作,这些引起中断的事件称为中断源,它们可能是来自外设的输入输出请求,也可能是计算机的一些异常事故或其它内部原因。

    中断:在运行一个程序的过程中,断续地以“插入”方式执行一些完成特定处理功能的程序段,这种处理方式称为中断。

    2. 中断的作用

    并行操作

    硬件故障报警与处理

    支持多道程序并发运行,提高计算机系统的运行效率

    支持实时处理功能

    3. 术语

    按中断源进行分类:发出中断请求的设备称为中断源。按中断源的不同,中断可分为

    内中断:即程序运行错误引起的中断

    外中断:即由外部设备、接口卡引起的中断

    软件中断:由写在程序中的语句引起的中断程序的执行,称为软件中断

    允许/禁止(开/关)中断: CPU通过指令限制某些设备发出中断请求,称为屏蔽中断。从CPU要不要接收中断即能不能限制某些中断发生的角度 ,中断可分为

    可屏蔽中断 :可被CPU通过指令限制某些设备发出中断请求的中断, 那是不是意味着进中断时disable整个中断,其实disable的都是可屏蔽中断?

    不可屏蔽中断:不允许屏蔽的中断如电源掉电

    中断允许触发器:在CPU内部设置一个中断允许触发器,只有该触发器置“1”,才允许中断;置“0”,不允许中断。

    指令系统中,开中断指令,使中断触发器置“1”

    关中断指令,使中断触发器置“0”

    中断优先级:为了管理众多的中断请求,需要按每个(类)中断处理的急迫程度,对中断进行分级管理,称其为中断优先级。在有多个中断请求时,总是响应与处理优先级高的设备的中断请求。

    中断嵌套:当CPU正在处理优先级较低的一个中断,又来了优先级更高的一个中断请求,则CPU先停止低优先级的中断处理过程,去响应优先级更高的中断请求,在优先级更高的中断处理完成之后,再继续处理低优先级的中断,这种情况称为中断嵌套。

    Intel的官方文档里将中断和异常理解为两种中断当前程序执行的不同机制。这是中断和异常的共同点。不同点在于:

    中断(interrupt)是异步的事件,典型的比如由I/O设备触发;异常(exception)是同步的事件,典型的比如处理器执行某条指令时发现出错了等等。

    中断又可以分为可屏蔽中断和非可屏蔽中断,异常又分为故障、陷阱和异常中止3种,它们的具体区别很多书籍和官方文档都解释的比较清楚这里不再赘述。

    关于它们的区别有两点是需要注意的:

    平常所说的屏蔽中断是不包括异常的,即异常不会因为CPU的IF位被清(关中断,指令:cli)而受影响,比如缺页异常,即使关了中断也会触发CPU的处理,回答了我上面红色部分疑问。

    通常说的int 80h这种系统调用使用的中断方式实际上硬件上是理解为异常处理的,因此也不会被屏蔽掉,这也很好理解,int 80h这种中断方式是程序里主动触发的,对于CPU来说属于同步事件,因此也就属于异常的范畴。

    4. 中断(异常)处理过程

    需要明确的一点是CPU对于中断和异常的具体处理机制本质上是完全一致的,即:

    当CPU收到中断或者异常的信号时,它会暂停执行当前的程序或任务,通过一定的机制跳转到负责处理这个信号的相关处理程序中,在完成对这个信号的处理后再跳回到刚才被打断的程序或任务中。这里只描述保护模式下的处理过程,搞清楚了保护模式下的处理过程(更复杂),实模式下的处理机制也就容易理解了。

    具体的处理过程如下:

    1)  中断响应的事前准备:

    系统要想能够应对各种不同的中断信号,总的来看就是需要知道每种信号应该由哪个中断服务程序负责以及这些中断服务程序具体是如何工作的。系统只有事前对这两件事都知道得很清楚,才能正确地响应各种中断信号和异常。

    系统将所有的中断信号统一进行了编号(一共256个:0~255),这个号称为中断向量,具体哪个中断向量表示哪种中断有的是规定好的,也有的是在给定范围内自行设定的。  中断向量和中断服务程序的对应关系主要是由IDT(中断向量表)负责。操作系统在IDT中设置好各种中断向量对应的中断描述符(一共有三类中断门描述符:任务门、中断门和陷阱门),留待CPU查询使用。而IDT本身的位置是由idtr保存的,当然这个地址也是由OS填充的。

    中断服务程序具体负责处理中断(异常)的代码是由软件,也就是操作系统实现的,这部分代码属于操作系统内核代码。也就是说从CPU检测中断信号到加载中断服务程序以及从中断服务程序中恢复执行被暂停的程序,这个流程基本上是硬件确定下来的,而具体的中断向量和服务程序的对应关系设置和中断服务程序的内容是由操作系统确定的。

    2) CPU检查是否有中断/异常信号

    CPU在执行完当前程序的每一条指令后,都会去确认在执行刚才的指令过程中中断控制器(如:8259A)是否发送中断请求过来,如果有那么CPU就会在相应的时钟脉冲到来时从总线上读取中断请求对应的中断向量[2]。

    对于异常和系统调用那样的软中断,因为中断向量是直接给出的,所以和通过IRQ(中断请求)线发送的硬件中断请求不同,不会再专门去取其对应的中断向量。

    3) 根据中断向量到IDT表中取得处理这个向量的中断程序的段选择符

    CPU根据得到的中断向量到IDT表里找到该向量对应的中断描述符,中断描述符里保存着中断服务程序的段选择符。

    4) 根据取得的段选择符到GDT中找相应的段描述符

    CPU使用IDT查到的中断服务程序的段选择符从GDT中取得相应的段描述符,段描述符里保存了中断服务程序的段基址和属性信息,此时CPU就得到了中断服务程序的起始地址。这里,CPU会根据当前cs寄存器里的CPL和GDT的段描述符的DPL,以确保中断服务程序是高于当前程序的,如果这次中断是编程异常(如:int 80h系统调用),那么还要检查CPL和IDT表中中断描述符的DPL,以保证当前程序有权限使用中断服务程序,这可以避免用户应用程序访问特殊的陷阱门和中断门[3]。

    5) CPU根据特权级的判断设定即将运行的中断服务程序要使用的栈的地址

    CPU会根据CPL和中断服务程序段描述符的DPL信息确认是否发生了特权级的转换,比如当前程序正运行在用户态,而中断程序是运行在内核态的,则意味着发生了特权级的转换,这时CPU会从当前程序的TSS信息(该信息在内存中的首地址存在TR寄存器中)里取得该程序的内核栈地址,即包括ss和esp的值,并立即将系统当前使用的栈切换成新的栈。这个栈就是即将运行的中断服务程序要使用的栈。紧接着就将当前程序使用的ss,esp压到新栈中保存起来。也就说比如当前在某个函数中,使用的栈,在中断发生时,需要切换新的栈。

    6) 保护当前程序的现场

    CPU开始利用栈保护被暂停执行的程序的现场:依次压入当前程序使用的eflags,cs,eip,errorCode(如果是有错误码的异常)信息。

    官方文档[1]给出的栈变化的示意图如下:

    7) 跳转到中断服务程序的第一条指令开始执行

    CPU利用中断服务程序的段描述符将其第一条指令的地址加载到cs和eip寄存器中,开始执行中断服务程序。这意味着先前的程序被暂停执行,中断服务程序正式开始工作。

    8) 中断服务程序处理完毕,恢复执行先前中断的程序

    在每个中断服务程序的最后,必须有中断完成返回先前程序的指令,这就是iret(或iretd)。程序执行这条返回指令时,会从栈里弹出先前保存的被暂停程序的现场信息,即eflags,cs,eip重新开始执行。

    展开全文
  • Linux 中断之中断处理浅析

    万次阅读 多人点赞 2019-01-17 14:15:09
    1. 中断的概念 中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的 CPU 暂时...软件对硬件进行配置后,软件期望等待硬件的某种状态(比如,收到了数据),这里两种方式,一种是轮询(pol...

    1. 中断的概念

    中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的 CPU 暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)。

    软件对硬件进行配置后,软件期望等待硬件的某种状态(比如,收到了数据),这里有两种方式,一种是轮询(polling): CPU 不断的去读硬件状态。另一种是当硬件完成某种事件后,给 CPU 一个中断,让 CPU 停下手上的事情,去处理这个中断。很显然,中断的交互方式提高了系统的吞吐。

    当 CPU 收到一个中断 (IRQ)的时候,会去执行该中断对应的处理函数(ISR)。普通情况下,会有一个中断向量表,向量表中定义了 CPU 对应的每一个外设资源的中断处理程序的入口,当发生对应的中断的时候, CPU 直接跳转到这个入口执行程序。也就是中断上下文。(注意:中断上下文中,不可阻塞睡眠)。

     

    2. Linux 中断 top/bottom

    玩过 MCU 的人都知道,中断服务程序的设计最好是快速完成任务并退出,因为此刻系统处于被中断中。但是在 ISR 中又有一些必须完成的事情,比如:清中断标志,读/写数据,寄存器操作等。

    在 Linux 中,同样也是这个要求,希望尽快的完成 ISR。但事与愿违,有些 ISR 中任务繁重,会消耗很多时间,导致响应速度变差。Linux 中针对这种情况,将中断分为了两部分:

    1. 上半部(top half):收到一个中断,立即执行,有严格的时间限制,只做一些必要的工作,比如:应答,复位等。这些工作都是在所有中断被禁止的情况下完成的

    2. 底半部(bottom half):能够被推迟到后面完成的任务会在底半部进行。在适合的时机,下半部会被开中断执行。(具体的机制在下一篇文章分析)

     

    3. 中断处理程序

    驱动程序可以使用接口:

    request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
    	    const char *name, void *dev)

    像系统申请注册一个中断处理程序。

    其中的参数:

    参数含义
    irq表了该中断的中断号,一般 CPU 的中断号都会事先定义好。
    handler中断发生后的 ISR
    flags中断标志( IRQF_DISABLED / IRQFSAMPLE_RANDOM / IRQF_TIMER / IRQF_SHARED)
    name 中断相关的设备 ASCII 文本,例如 "keyboard",这些名字会在 /proc/irq 和 /proc/interrupts 文件使用
    dev用于共享中断线,传递驱动程序的设备结构。非共享类型的中断,直接设置成为 NULL

     

     

     

     

     

     

     

    中断标志 flag 的含义:

    标志含义
    IRQF_DISABLED设置这个标志的话,意味着内核在处理这个 ISR 期间,要禁止其他中断(多数情况不使用这个)
    IRQFSAMPLE_RANDOM表明这个设备产生的中断对内核熵池有贡献
    IRQF_TIMER为系统定时器准备的标志
    IRQF_SHARED表明多个中断处理程序之间共享中断线。同一个给定的线上注册每个处理程序,必须设置这个

     

     

     

     

     

     

    调用 request _irq 成功执行返回 0。常见错误是 -EBUSY,表示给定的中断线已经在使用(或者没有指定 IRQF_SHARED)

    注意:request_irq 函数可能引起睡眠,所以不允许在中断上下文或者不允许睡眠的代码中调用

     

    释放中断:

    const void *free_irq(unsigned int irq, void *dev_id)

    用于释放中断处理函数。

    注意:Linux 中的中断处理程序是无须重入的。当给定的中断处理程序正在执行的时候,其中断线在所有的处理器上都会被屏蔽掉,以防在同一个中断线上又接收到另一个新的中断。通常情况下,除了该中断的其他中断都是打开的,也就是说其他的中断线上的重点都能够被处理,但是当前的中断线总是被禁止的,故,同一个中断处理程序是绝对不会被自己嵌套的

     

    4. 中断上下文

    与进程上下文不一样,内核执行中断服务程序的时候,处于中断上下文。中断处理程序并没有自己的独立的栈,而是使用了内核栈,其大小一般是有限制的(32bit 机器 8KB)。所以其必须短小精悍。同时中断服务程序是打断了正常的程序流程,这一点上也必须保证快速的执行。同时中断上下文中是不允许睡眠,阻塞的。

    中断上下文不能睡眠的原因是

    1、 中断处理的时候,不应该发生进程切换,因为在中断context中,唯一能打断当前中断handler的只有更高优先级的中断,它不会被进程打断,如果在 中断context中休眠,则没有办法唤醒它,因为所有的wake_up_xxx都是针对某个进程而言的,而在中断context中,没有进程的概念,没 有一个task_struct(这点对于softirq和tasklet一样),因此真的休眠了,比如调用了会导致block的例程,内核几乎肯定会死。

    2、schedule()在切换进程时,保存当前的进程上下文(CPU寄存器的值、进程的状态以及堆栈中的内容),以便以后恢复此进程运行。中断发生后,内核会先保存当前被中断的进程上下文(在调用中断处理程序后恢复);

    但在中断处理程序里,CPU寄存器的值肯定已经变化了吧(最重要的程序计数器PC、堆栈SP等),如果此时因为睡眠或阻塞操作调用了schedule(),则保存的进程上下文就不是当前的进程context了.所以不可以在中断处理程序中调用schedule()。

    3、内核中schedule()函数本身在进来的时候判断是否处于中断上下文:

    if(unlikely(in_interrupt()))

    BUG();

    因此,强行调用schedule()的结果就是内核BUG。

    4、中断handler会使用被中断的进程内核堆栈,但不会对它有任何影响,因为handler使用完后会完全清除它使用的那部分堆栈,恢复被中断前的原貌。

    5、处于中断context时候,内核是不可抢占的。因此,如果休眠,则内核一定挂起。

     

    5. 举例

    比如 RTC 驱动程序 (drivers/char/rtc.c)。在 RTC 驱动的初始化阶段,会调用到 rtc_init 函数:

    module_init(rtc_init);

    在这个初始化函数中调用到了 request_irq 用于申请中断资源,并注册服务程序:

    static int __init rtc_init(void)
    {
    ...
        rtc_int_handler_ptr = rtc_interrupt;
    ...
        request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)
    ...
    }

    RTC_IRQ 是中断号,和处理器绑定。

    rtc_interrupt 是中断处理程序:

    static irqreturn_t rtc_interrupt(int irq, void *dev_id)
    {
    	/*
    	 *	Can be an alarm interrupt, update complete interrupt,
    	 *	or a periodic interrupt. We store the status in the
    	 *	low byte and the number of interrupts received since
    	 *	the last read in the remainder of rtc_irq_data.
    	 */
    
    	spin_lock(&rtc_lock);
    	rtc_irq_data += 0x100;
    	rtc_irq_data &= ~0xff;
    	if (is_hpet_enabled()) {
    		/*
    		 * In this case it is HPET RTC interrupt handler
    		 * calling us, with the interrupt information
    		 * passed as arg1, instead of irq.
    		 */
    		rtc_irq_data |= (unsigned long)irq & 0xF0;
    	} else {
    		rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
    	}
    
    	if (rtc_status & RTC_TIMER_ON)
    		mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
    
    	spin_unlock(&rtc_lock);
    
    	wake_up_interruptible(&rtc_wait);
    
    	kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
    
    	return IRQ_HANDLED;
    }
    

    每次收到 RTC 中断,就会调用进这个函数。

     

    6. 中断处理流程

    发生中断时,CPU执行异常向量vector_irq的代码, 即异常向量表中的中断异常的代码,它是一个跳转指令,跳去执行真正的中断处理程序,在vector_irq里面,最终会调用中断处理的总入口函数。

    C 语言的入口为 : asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

    asmlinkage void __exception_irq_entry
    asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
    {
    	handle_IRQ(irq, regs);
    }

    该函数的入参 irq 为中断号。

    asm_do_IRQ -> handle_IRQ

    void handle_IRQ(unsigned int irq, struct pt_regs *regs)
    {
    	__handle_domain_irq(NULL, irq, false, regs);
    }

    handle_IRQ -> __handle_domain_irq

    int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
    			bool lookup, struct pt_regs *regs)
    {
    	struct pt_regs *old_regs = set_irq_regs(regs);
    	unsigned int irq = hwirq;
    	int ret = 0;
    
    	irq_enter();
    
    #ifdef CONFIG_IRQ_DOMAIN
    	if (lookup)
    		irq = irq_find_mapping(domain, hwirq);
    #endif
    
    	/*
    	 * Some hardware gives randomly wrong interrupts.  Rather
    	 * than crashing, do something sensible.
    	 */
    	if (unlikely(!irq || irq >= nr_irqs)) {
    		ack_bad_irq(irq);
    		ret = -EINVAL;
    	} else {
    		generic_handle_irq(irq);
    	}
    
    	irq_exit();
    	set_irq_regs(old_regs);
    	return ret;
    }

    这里请注意:

    先调用了 irq_enter 标记进入了硬件中断:

    irq_enter是更新一些系统的统计信息,同时在__irq_enter宏中禁止了进程的抢占。虽然在产生IRQ时,ARM会自动把CPSR中的I位置位,禁止新的IRQ请求,直到中断控制转到相应的流控层后才通过local_irq_enable()打开。那为何还要禁止抢占?这是因为要考虑中断嵌套的问题,一旦流控层或驱动程序主动通过local_irq_enable打开了IRQ,而此时该中断还没处理完成,新的irq请求到达,这时代码会再次进入irq_enter,在本次嵌套中断返回时,内核不希望进行抢占调度,而是要等到最外层的中断处理完成后才做出调度动作,所以才有了禁止抢占这一处理

    再调用 generic_handle_irq

    最后调用 irq_exit 删除进入硬件中断的标记

    __handle_domain_irq -> generic_handle_irq

    int generic_handle_irq(unsigned int irq)
    {
    	struct irq_desc *desc = irq_to_desc(irq);
    
    	if (!desc)
    		return -EINVAL;
    	generic_handle_irq_desc(desc);
    	return 0;
    }
    EXPORT_SYMBOL_GPL(generic_handle_irq);

    首先在函数 irq_to_desc 中根据发生中断的中断号,去取出它的 irq_desc 中断描述结构,然后调用 generic_handle_irq_desc

    static inline void generic_handle_irq_desc(struct irq_desc *desc)
    {
    	desc->handle_irq(desc);
    }

    这里调用了 handle_irq 函数。

    所以,在上述流程中,还需要分析 irq_to_desc  流程:

    struct irq_desc *irq_to_desc(unsigned int irq)
    {
    	return (irq < NR_IRQS) ? irq_desc + irq : NULL;
    }
    EXPORT_SYMBOL(irq_to_desc);

    NR_IRQS 是支持的总的中断个数,当然,irq 不能够大于这个数目。所以返回 irq_desc + irq。

    irq_desc 是一个全局的数组:

    struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
    	[0 ... NR_IRQS-1] = {
    		.handle_irq	= handle_bad_irq,
    		.depth		= 1,
    		.lock		= __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
    	}
    };

    这里是这个数组的初始化的地方。所有的 handle_irq 函数都被初始化成为了 handle_bad_irq。

    细心的观众可能发现了,调用这个 desc->handle_irq(desc) 函数,并不是咱们注册进去的中断处理函数啊,因为两个函数的原型定义都不一样。这个 handle_irq irq_flow_handler_t 类型,而我们注册进去的服务程序是 irq_handler_t,这两个明显不是同一个东西,所以这里我们还需要继续分析。

     

    6.1 中断相关的数据结构

    Linux 中断相关的数据结构有 3 个

    结构名称作用
    irq_descIRQ 的软件层面上的资源描述
    irqactionIRQ 的通用操作
    irq_chip对应每个芯片的具体实现

     

     

     

     

     

    irq_desc 结构如下

    struct irq_desc {
    	struct irq_common_data	irq_common_data;
    	struct irq_data		irq_data;
    	unsigned int __percpu	*kstat_irqs;
    	irq_flow_handler_t	handle_irq;
    #ifdef CONFIG_IRQ_PREFLOW_FASTEOI
    	irq_preflow_handler_t	preflow_handler;
    #endif
    	struct irqaction	*action;	/* IRQ action list */
    	unsigned int		status_use_accessors;
    	unsigned int		core_internal_state__do_not_mess_with_it;
    	unsigned int		depth;		/* nested irq disables */
    	unsigned int		wake_depth;	/* nested wake enables */
    	unsigned int		irq_count;	/* For detecting broken IRQs */
    	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
    	unsigned int		irqs_unhandled;
    	atomic_t		threads_handled;
    	int			threads_handled_last;
    	raw_spinlock_t		lock;
    	struct cpumask		*percpu_enabled;
    	const struct cpumask	*percpu_affinity;
    #ifdef CONFIG_SMP
    	const struct cpumask	*affinity_hint;
    	struct irq_affinity_notify *affinity_notify;
    #ifdef CONFIG_GENERIC_PENDING_IRQ
    	cpumask_var_t		pending_mask;
    #endif
    #endif
    	unsigned long		threads_oneshot;
    	atomic_t		threads_active;
    	wait_queue_head_t       wait_for_threads;
    #ifdef CONFIG_PM_SLEEP
    	unsigned int		nr_actions;
    	unsigned int		no_suspend_depth;
    	unsigned int		cond_suspend_depth;
    	unsigned int		force_resume_depth;
    #endif
    #ifdef CONFIG_PROC_FS
    	struct proc_dir_entry	*dir;
    #endif
    #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
    	struct dentry		*debugfs_file;
    	const char		*dev_name;
    #endif
    #ifdef CONFIG_SPARSE_IRQ
    	struct rcu_head		rcu;
    	struct kobject		kobj;
    #endif
    	struct mutex		request_mutex;
    	int			parent_irq;
    	struct module		*owner;
    	const char		*name;
    } ____cacheline_internodealigned_in_smp;

     

    irqaction 结构如下:

    /**
     * struct irqaction - per interrupt action descriptor
     * @handler:	interrupt handler function
     * @name:	name of the device
     * @dev_id:	cookie to identify the device
     * @percpu_dev_id:	cookie to identify the device
     * @next:	pointer to the next irqaction for shared interrupts
     * @irq:	interrupt number
     * @flags:	flags (see IRQF_* above)
     * @thread_fn:	interrupt handler function for threaded interrupts
     * @thread:	thread pointer for threaded interrupts
     * @secondary:	pointer to secondary irqaction (force threading)
     * @thread_flags:	flags related to @thread
     * @thread_mask:	bitmask for keeping track of @thread activity
     * @dir:	pointer to the proc/irq/NN/name entry
     */
    struct irqaction {
    	irq_handler_t		handler;
    	void			*dev_id;
    	void __percpu		*percpu_dev_id;
    	struct irqaction	*next;
    	irq_handler_t		thread_fn;
    	struct task_struct	*thread;
    	struct irqaction	*secondary;
    	unsigned int		irq;
    	unsigned int		flags;
    	unsigned long		thread_flags;
    	unsigned long		thread_mask;
    	const char		*name;
    	struct proc_dir_entry	*dir;
    } ____cacheline_internodealigned_in_smp;
    

     

    irq_chip 描述如下:

    /**
     * struct irq_chip - hardware interrupt chip descriptor
     *
     * @parent_device:	pointer to parent device for irqchip
     * @name:		name for /proc/interrupts
     * @irq_startup:	start up the interrupt (defaults to ->enable if NULL)
     * @irq_shutdown:	shut down the interrupt (defaults to ->disable if NULL)
     * @irq_enable:		enable the interrupt (defaults to chip->unmask if NULL)
     * @irq_disable:	disable the interrupt
     * @irq_ack:		start of a new interrupt
     * @irq_mask:		mask an interrupt source
     * @irq_mask_ack:	ack and mask an interrupt source
     * @irq_unmask:		unmask an interrupt source
     * @irq_eoi:		end of interrupt
     * @irq_set_affinity:	Set the CPU affinity on SMP machines. If the force
     *			argument is true, it tells the driver to
     *			unconditionally apply the affinity setting. Sanity
     *			checks against the supplied affinity mask are not
     *			required. This is used for CPU hotplug where the
     *			target CPU is not yet set in the cpu_online_mask.
     * @irq_retrigger:	resend an IRQ to the CPU
     * @irq_set_type:	set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
     * @irq_set_wake:	enable/disable power-management wake-on of an IRQ
     * @irq_bus_lock:	function to lock access to slow bus (i2c) chips
     * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
     * @irq_cpu_online:	configure an interrupt source for a secondary CPU
     * @irq_cpu_offline:	un-configure an interrupt source for a secondary CPU
     * @irq_suspend:	function called from core code on suspend once per
     *			chip, when one or more interrupts are installed
     * @irq_resume:		function called from core code on resume once per chip,
     *			when one ore more interrupts are installed
     * @irq_pm_shutdown:	function called from core code on shutdown once per chip
     * @irq_calc_mask:	Optional function to set irq_data.mask for special cases
     * @irq_print_chip:	optional to print special chip info in show_interrupts
     * @irq_request_resources:	optional to request resources before calling
     *				any other callback related to this irq
     * @irq_release_resources:	optional to release resources acquired with
     *				irq_request_resources
     * @irq_compose_msi_msg:	optional to compose message content for MSI
     * @irq_write_msi_msg:	optional to write message content for MSI
     * @irq_get_irqchip_state:	return the internal state of an interrupt
     * @irq_set_irqchip_state:	set the internal state of a interrupt
     * @irq_set_vcpu_affinity:	optional to target a vCPU in a virtual machine
     * @ipi_send_single:	send a single IPI to destination cpus
     * @ipi_send_mask:	send an IPI to destination cpus in cpumask
     * @flags:		chip specific flags
     */
    struct irq_chip {
    	struct device	*parent_device;
    	const char	*name;
    	unsigned int	(*irq_startup)(struct irq_data *data);
    	void		(*irq_shutdown)(struct irq_data *data);
    	void		(*irq_enable)(struct irq_data *data);
    	void		(*irq_disable)(struct irq_data *data);
    
    	void		(*irq_ack)(struct irq_data *data);
    	void		(*irq_mask)(struct irq_data *data);
    	void		(*irq_mask_ack)(struct irq_data *data);
    	void		(*irq_unmask)(struct irq_data *data);
    	void		(*irq_eoi)(struct irq_data *data);
    
    	int		(*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
    	int		(*irq_retrigger)(struct irq_data *data);
    	int		(*irq_set_type)(struct irq_data *data, unsigned int flow_type);
    	int		(*irq_set_wake)(struct irq_data *data, unsigned int on);
    
    	void		(*irq_bus_lock)(struct irq_data *data);
    	void		(*irq_bus_sync_unlock)(struct irq_data *data);
    
    	void		(*irq_cpu_online)(struct irq_data *data);
    	void		(*irq_cpu_offline)(struct irq_data *data);
    
    	void		(*irq_suspend)(struct irq_data *data);
    	void		(*irq_resume)(struct irq_data *data);
    	void		(*irq_pm_shutdown)(struct irq_data *data);
    
    	void		(*irq_calc_mask)(struct irq_data *data);
    
    	void		(*irq_print_chip)(struct irq_data *data, struct seq_file *p);
    	int		(*irq_request_resources)(struct irq_data *data);
    	void		(*irq_release_resources)(struct irq_data *data);
    
    	void		(*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
    	void		(*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
    
    	int		(*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
    	int		(*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
    
    	int		(*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
    
    	void		(*ipi_send_single)(struct irq_data *data, unsigned int cpu);
    	void		(*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
    
    	unsigned long	flags;
    };

    irq_chip 是一串和芯片相关的函数指针,这里定义的非常的全面,基本上和 IRQ 相关的可能出现的操作都全部定义进去了,具体根据不同的芯片,需要在不同的芯片的地方去初始化这个结构,然后这个结构会嵌入到通用的 IRQ 处理软件中去使用,使得软件处理逻辑和芯片逻辑完全的分开。

    好,我们接下来继续前进。

     

    6.2 初始化 Chip 相关的 IRQ

    众所周知,启动的时候,C 语言从 start_kernel 开始,在这里面,调用了和 machine 相关的 IRQ 的初始化 init_IRQ()

    asmlinkage __visible void __init start_kernel(void)
    {
    	char *command_line;
    	char *after_dashes;
    
    .....
    
        early_irq_init();
        init_IRQ();
    
    .....
    
    }

    在 init_IRQ 中,调用了 machine_desc->init_irq()

    void __init init_IRQ(void)
    {
    	int ret;
    
    	if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
    		irqchip_init();
    	else
    		machine_desc->init_irq();
    
    	if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&
    	    (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {
    		if (!outer_cache.write_sec)
    			outer_cache.write_sec = machine_desc->l2c_write_sec;
    		ret = l2x0_of_init(machine_desc->l2c_aux_val,
    				   machine_desc->l2c_aux_mask);
    		if (ret && ret != -ENODEV)
    			pr_err("L2C: failed to init: %d\n", ret);
    	}
    
    	uniphier_cache_init();
    }
    

    machine_desc->init_irq() 完成对中断控制器的初始化,为每个irq_desc结构安装合适的流控handler,为每个irq_desc结构安装irq_chip指针,使他指向正确的中断控制器所对应的irq_chip结构的实例,同时,如果该平台中的中断线有多路复用(多个中断公用一个irq中断线)的情况,还应该初始化irq_desc中相应的字段和标志,以便实现中断控制器的级联。

    这里初始化的时候回调用到具体的芯片相关的中断初始化的地方。

    例如:

    int __init s5p_init_irq_eint(void)
    {
    	int irq;
     
    	for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)
    		irq_set_chip(irq, &s5p_irq_vic_eint);
     
    	for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
    		irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
    		set_irq_flags(irq, IRQF_VALID);
    	}
     
    	irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
    	return 0;
    }

    而在这些里面,都回去调用类似于:

    void
    irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
    			      irq_flow_handler_t handle, const char *name);
    
    irq_set_handler(unsigned int irq, irq_flow_handler_t handle)
    {
    	__irq_set_handler(irq, handle, 0, NULL);
    }
    
    static inline void
    irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)
    {
    	__irq_set_handler(irq, handle, 1, NULL);
    }
    
    void
    irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
    				 void *data);
    
    

    这些函数定义在 include/linux/irq.h 文件。是对芯片初始化的时候可见的 APIs,用于指定中断“流控”中的 :

    irq_flow_handler_t handle

    也就是中断来的时候,最后那个函数调用。

    中断流控函数,分几种,电平触发的中断,边沿触发的,等:

    /*
     * Built-in IRQ handlers for various IRQ types,
     * callable via desc->handle_irq()
     */
    extern void handle_level_irq(struct irq_desc *desc);
    extern void handle_fasteoi_irq(struct irq_desc *desc);
    extern void handle_edge_irq(struct irq_desc *desc);
    extern void handle_edge_eoi_irq(struct irq_desc *desc);
    extern void handle_simple_irq(struct irq_desc *desc);
    extern void handle_untracked_irq(struct irq_desc *desc);
    extern void handle_percpu_irq(struct irq_desc *desc);
    extern void handle_percpu_devid_irq(struct irq_desc *desc);
    extern void handle_bad_irq(struct irq_desc *desc);
    extern void handle_nested_irq(unsigned int irq);
    

    而在这些处理函数里,会去调用到 : handle_irq_event 

    比如:

    /**
     *	handle_level_irq - Level type irq handler
     *	@desc:	the interrupt description structure for this irq
     *
     *	Level type interrupts are active as long as the hardware line has
     *	the active level. This may require to mask the interrupt and unmask
     *	it after the associated handler has acknowledged the device, so the
     *	interrupt line is back to inactive.
     */
    void handle_level_irq(struct irq_desc *desc)
    {
    	raw_spin_lock(&desc->lock);
    	mask_ack_irq(desc);
    
    	if (!irq_may_run(desc))
    		goto out_unlock;
    
    	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
    
    	/*
    	 * If its disabled or no action available
    	 * keep it masked and get out of here
    	 */
    	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
    		desc->istate |= IRQS_PENDING;
    		goto out_unlock;
    	}
    
    	kstat_incr_irqs_this_cpu(desc);
    	handle_irq_event(desc);
    
    	cond_unmask_irq(desc);
    
    out_unlock:
    	raw_spin_unlock(&desc->lock);
    }
    

    而这个 handle_irq_event 则是调用了处理,handle_irq_event_percpu

    irqreturn_t handle_irq_event(struct irq_desc *desc)
    {
    	irqreturn_t ret;
    
    	desc->istate &= ~IRQS_PENDING;
    	irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    	raw_spin_unlock(&desc->lock);
    
    	ret = handle_irq_event_percpu(desc);
    
    	raw_spin_lock(&desc->lock);
    	irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    	return ret;
    }
    

    handle_irq_event_percpu->__handle_irq_event_percpu-> 【action->handler()

    这里终于看到了调用 的地方了,就是咱们通过 request_irq 注册进去的函数


     

    7. /proc/interrupts

    这个 proc 下放置了对应中断号的中断次数和对应的 dev-name

     

    展开全文
  • 为了对计算机的硬件(键盘,硬盘,鼠标等)进行管理,内核需要和这些硬件通信。一种方式是使用轮训(polling)的方式,这种方式周期性地查看所有硬件设备的状态并做相应处理,这会造成很多不必要的系统开销。Linux...

    中断

    为了对计算机的硬件(键盘,硬盘,鼠标,网卡等)进行管理,内核需要和这些硬件通信。一种方式是使用轮询(polling)的方式,这种方式周期性地查看所有硬件设备的状态并做相应处理,这会造成很多不必要的系统开销。Linux内核使用中断的方式来管理硬件设备,中断本质上是一种电信号,设备通过和中断控制器引脚相连的总线发出电信号来发出中断。中断控制器是一种控制芯片,多个设备的中断请求线同时连接到中断控制器上,如果多个设备同时发出中断信号,中断控制器根据优先级选择其中一个发送给处理器处理器,处理器收到中断请求后,就中断当前正在执行的任务,进行中断处理。内核通过中断号(中断号是系统为每个中断请求线interrupt request line分配的编号)来区分不同的设备产生的中断,从而执行对应的中断处理程序。

    人们经常把中断和异常(Exception)放在一起讨论,前者是异步中断,后者是同步中断。同步是指在一个处理器指令执行完毕后产生的(是由处理器产生的),异步指的是在任何时候都有可能发生,和处理器是否完成当前指令无关,当然处理器会将正在执行的指令执行完毕才会去检查是否有中断请求。异常又可以分为处理器检测到的异常(Processor-detected exception)和可编程的异常(programmmed exception),前者又分为fault, trap, abort三种,后者是为了进入内核态(软中断/系统调用)。缺页异常和除以0都属于fault。异常的具体分类在网上看到了很多,各有各的说法,看得我大脑都混乱了,这里的分类参考的是哥伦比亚大学的一份上课PPT的分类法。

    中断处理程序

    当接收到一个中断时,内核会执行中断处理程序(interrupt handler),每个可以产生中断的设备都有一个对应的中断处理程序。中断处理程序是设备驱动的一部分,中断处理程序的函数声明必须遵照规定的格式,中断处理程序本质上是一个函数,和内核其他函数的区别在于中断处理程序是由内核响应中断时调用的,它运行在一个被称为中断上下文的特殊上下文中。中断上下文中不能被阻塞,所以有时候也会被称为原子上下文。

    中断处理程序必须快速地完成执行,这样才能快速地对中断做出响应的同时确保被中断抢占的代码可以尽快地恢复执行。但是中断处理程序往往有大量工作要做,比如网卡的中断处理程序就需要将网络中的数据包从硬件上复制到内存中,处理数据包,最后将数据包交给合适的协议栈或者应用程序。

    上半部和下半部

    中断程序有两个目标:快速完成执行和处理大量工作。很显然,这两个目标是冲突的。为了能够同时达到这两个目标,对中断的响应被分成了两部分:上半部和下半部。上半部是中断处理程序,上半部在接受到中断请求后立刻执行,但是只处理对时延敏感的任务,如对中断进行应答(通过给中断线置低电平告诉设备处理器已经收到中断了)或者复位硬件。对时延不敏感的任务都被放在了下半部,延后执行(下一篇博文将会介绍下半部)。

    还是以网卡为例,网卡收到数据包,发出中断请求,告诉内核收到了数据包。因为网卡上接受数据包的缓冲区大小固定,并且比系统内存小得多,为了避免网卡缓冲区溢出丢包,内核需要快速的完成以下工作:通知网卡中断已经响应(通常是给网卡的寄存器复位)并将数据包拷贝到系统内存,到此中断处理程序(上半部)完成,然后将处理器的使用权交还给内核其他部分(调度程序这个时候就要负责选择下一个执行的进程了,详见Linux内核学习笔记(六)进程调度

    中断处理程序的注册、注销和实现

    注册

    每个设备都有一个对应的设备驱动,如果该设备使用中断(大部分都会使用),那么设备驱动必须要注册一个中断处理程序。注册中断处理程序的函数是 request_irq(),声明在<linux/interrupt.h>中,声明如下:

    /* request_irq: allocate a given interrupt line */
    int request_irq(unsigned int irq, irq_handler_t handler,
                    unsigned long flags, const char *name, void *dev)

    参数irq是对应的终端号。系统时钟和键盘这样的传统PC设备的中断号通常是固定的,对于大部分设备,这个值是动态确定的(通过探测或者编程方式)。
    参数handler是一个函数指针,指向要注册的中断处理程序。

    参数flags是作为bitmask使用,规定了中断和中断处理程序的一些属性,比较重要的有:

    • IRQF_DISABLED 内核在执行该中断处理程序时会禁止所有中断。如果没有这个,内核只会禁止当前的中断处理程序的中断请求线,其他中断请求线仍然可以发出中断,这意味着当前中断处理程序可能会被其他优先级更高的中断请求线的处理程序抢占,这就也叫做中断嵌套。
    • IRQF_SAMPLE_RANDOM 该设备产生的中断会为内核熵池(entropy pool)作出贡献,内核熵池负责从各种随机事件(比如中断)中衍生出真正的随机数。
    • IRQF_SHARED 这个标志表示该中断线可以被多个中断处理程序共享(即多个设备共用同一个中断线,个人感觉一个设备注册多个共享中断线的中断处理程序也是有可能的,但是估计不会有人这样做)

    参数name用于传入设备的名字。

    参数dev用于区分共享同一个中断线的多个中断处理程序(注销共享中断线的其中一个中断处理程序时要用到)以及给中断处理程序传递有价值的数据(实践中通常会传递设备驱动用来表示设备的device结构)。

    如果irq对应的中断线当前处于禁用状态(说明没有中断处理程序注册在该中断线上),request_irq()还会激活该中断线。因为request_irq()可能阻塞,所以不能在中断上下文(interrupt context,稍后会详细介绍)中调用。并且无论IRQF_DISABLED是否被设置,内核都会禁用当前中断处理程序的中断线,所以共享同一个中断线的中断处理程序不会发生嵌套或者重入(后面有详细讨论)。

    注销

    当卸载一个设备的时候,需要注销该设备的中断处理程序。注销函数是

    void free_irq(unsigned int irq, void *dev)

    如果中断号irq对应的中断线不是共享的,free_irq()会将irq的中断处理程序移除,并禁用irq对应的中断线。如果中断号irq是共享的,free_irq()会将dev对应的中断处理程序移除,如果移除的中断处理程序是irq的最后一个中断处理程序,那么free_irq()会同时禁用irq对应的中断线。因为free_irq()可能被阻塞,所以同样不能在中断上下文中调用。

    实现

    中断处理程序的声明规范如下:

    static irqreturn_t intr_handler(int irq, void *dev)

    irq是该中断处理程序要处理的中断请求线的中断号。dev和传递给request_irq()的dev是一样的,用于区分共享中断线的中断处理程序。dev可以指向中断处理程序使用的数据结构,通常驱动中用于表示设备的device结构,这样既能保证唯一性,也可以给中断处理程序提供有价值的数据。中断处理程序的返回值irqreturen_t实质上就是int,但是只有两种返回值:IRQ_NONE和IRQ_HANDLED。中断处理程序被调用时如果发现不是自己的设备发出的中断,就会返回IRQ_NONE。如果判断是自己的设备发出的中断时,就会在处理后返回IRQ_HANDLED。

    中断处理程序的工作内容完全取决于设备和产生中断的原因,但是一个最基本的任务是告诉设备中断已经被响应(中断应答)。复杂的设备通常需要发送、接受数据以及执行额外的工作。驱动程序的编写者需要决定哪些任务必须放在上半部,哪些任务可以放在下半部,能放在下半部的任务要尽可能地放在下半部。

    中断上下文

    当执行一个中断处理程序时,内核处于中断上下文(interrupt context)中。与中断上下文相对的是进程上下文,进程上下文是内核所处的工作模式(注意只有在内核态才可能处于进程上下文),在进程上下文中,内核代表进程执行——比如执行系统调用和运行内核线程,此时可以通过current宏访问当前进程。在进程上下文中可以睡眠。(我个人的理解是中断处理不是一个进程,也没有关联的进程,只有关联的中断。而系统调用或者内核线程执行时有关联的进程,所以是在进程上下文中。此外《Linux Kernel Development 3rd》的作者Robert Love在Quora上亲自回答了进程上下文的问题,说得很详细,可以点击去看一下。)

    中断上下文不和一个进程关联(虽然current宏指向被中断的进程),因为没有一个后备的关联进程,因为无法被再次调度,所以中断上下文不能睡眠(我个人对此的理解是因为没有关联的进程,所以没有可用于切换时保存上下文数据的对象。那为什么不让中断处理程序关联一个进程呢,因为这样的话,虽然可以切换出去了,但是在中断处理程序(或者说上半部)中的任务是时延敏感的,必须立刻执行完毕,所以不能切换出去,那么也就没必要关联一个进程了)。所以不能在中断上下文中调用可能睡眠的函数,这限制了中断处理程序的能力(可以做的事情)。

    中断处理程序使用自己的栈——中断栈(interrupt stack)。中断栈每个处理器一个,栈大小为一个页大小(在32bit机器中通常为4KB)。早起内核版本中,中断程序共用被中断的进程的内核栈。

    一个中断处理的详细过程

    中断处理程序的实现是和体系结构、处理器、中断控制器类型、机器本身密切相关的,下图展示了从硬件发出中断到中断处理返回的完整流程图

    中断处理流程图

    硬件设备通过和中断控制器连接的总线发出电信号来向中断控制器发出中断,如果中断请求线是激活的(中断线是可以被屏蔽的),中断控制器将中断发送给处理器,除非处理器禁用中断了(这也同样是可能的,稍后会说),否则,处理器会在执行完当前指令后,禁用处理器中断,然后跳转到预先设置好的内存位置,并执行那里的代码。这个位置是内核设置的处理中断请求的程序的入口点,入口点是中断在内核中旅行的起点。

    入口点会将中断号和被中断进程的寄存器值保存到中断栈中,然后调用do_IRQ(),do_IRQ()从中断栈中提取中断号,然后给该中断线发送一个中断应答信号(其实就是让CPU回送一个低电平的电信号),并禁用该中断线,然后调用handle_IRQ_event()。

    handle_IRQ_event()先检查IRQF_DISABLE是否被设置,如果没有设置,就重新打开处理器中断(处理器收到中断后把处理器中断禁用了),需要注意的是当前的中断线仍然是禁用的。然后handle_IRQ_event()会按序调用注册在该中断线上的所有中断处理程序,每个中断处理程序都要先检查自己的设备是否发出了中断,如果没有立刻返回,返回值为IRQ_NONE,如果有就进行中断处理,最后返回IRQ_HANDLED。通常这种判断是通过查看设备上的状态寄存器来判断的。(补充说明一点:多个设备共享一个中断线时,如果该中断线产生了中断,那么该中断线上可能只有一个设备发出了中断,也有可能有多个设备同时发出了中断(其实就是中断线被置了高电平),所以该中断线上的每个中断处理程序都会被调用一次)

    查看中断系统的状态 /proc/interrupts

    查看/proc/interrupts可以获得当前系统中注册的中断以及他们的统计信息,包括被注册过的中断号、每个CPU收到的该中断号的次数以及该中断号的中断处理程序的名字、设备的名字。

    参考资料

    《Linux Kernel Development 3rd Edition》
    《Understanding The Linux Kernel 3rd Edition》

    展开全文
  • 展开全部1)中断e69da5e6ba903231313335323631343130323136353331333431366338响应的事前准备:系统要想能够应对各种不同的中断信号,总的来看就是需要知道每种信号应该由哪个中断服务程序负责以及这些中断服务程序...
  • 中断及中断处理过程

    万次阅读 多人点赞 2019-07-24 22:00:23
    1. 中断和异常的概念区别 Intel的官方文档里将中断和异常理解为两种中断当前程序执行的不同机制。这是中断和异常的共同点。 不同点在于: 中断(interrupt)是异步的事件,典型的比如由I/O设备触发;异常...
  • 中断处理的一般过程

    千次阅读 2021-07-03 22:00:18
    中断处理包括一下几个步骤: 1、中断请求 8088/8086CPU的NMI为边沿触发,INTR为电平触发。在中断请求被响应之前会一直发送中断请求。 2、中断源识别 当系统同时多个中断源发出的中断请求时,系统往往只能相应...
  • Linux 中断处理流程

    千次阅读 2019-03-20 21:09:08
    《微机原理及应用》 《Linux 内核完全注释》 概述指导 整体流程: 硬件流程 硬件结构 类似下面这个结构,不同的软件中断接在中断处理控制器 8259A 上,则中断控制器再唤醒 CPU 处理中断,中断控制器会传一个中断号给...
  • 第7章 中断和中断处理

    千次阅读 2022-02-23 22:46:41
    文章目录第7章 中断和中断处理中断中断处理程序上半部和下半部 第7章 中断和中断处理 操作系统内核的核心任务,都包含对连接到计算机上的硬件设备进行管理。轮询(polling)可能会是一种解决办法,但是会做许多...
  • 中断处理过程

    万次阅读 2019-08-13 16:05:45
    概念 中断全过程指的是从中断源发出中断请求开始,CPU响应这个请求,现行程序被中断,转至中断服务程序,直到中断...大体上可以把中断全过程分为5个阶段:中断请求、中断判优、中断响应、中断处理和中断返回。 ...
  • 一,中断的定义 二 ,中断处理的过程 三,8086/8088CPU可以处理256种不同类型的终端 四,中断服务程序的设计方法 五中断向量表的建立
  • 12.键盘中断处理

    万次阅读 2018-11-13 18:35:12
    上一节我们实现了键盘中断事件调用显示所有可显示字符的字体图形显示,对中断处理机制基本的认识。 目标 1.实现键盘扫描码字体图标显示。当键盘上的一个按键按下时,键盘会发送一个中断信号给CPU,与此同时,键盘...
  • 中断和中断处理(一)

    千次阅读 2015-07-13 07:45:34
    处理器在接收到中断后,会马上向操作系统反映此信号的到来,然后就u由操作系统来处理这些新到来的数据。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标志。这些中断值被称为中断请求线(IRQ)。中断是...
  • 中断处理的基本过程

    万次阅读 多人点赞 2020-03-24 15:07:49
    中断处理的基本过程包括中断请求、中断判优、中断响应、中断服务 和中断返回等五个阶段。 1.中断请求阶段 1)发生在CPU内部的中断(内部中断),不需要中断请求,CPU内部的中断控制逻辑直接接收处理。 2)外部中断...
  • Linux中的中断处理

    千次阅读 2019-02-08 09:19:27
    Linux中的中断处理
  • 中断响应优先级和中断处理优先级的区别 中断响应优先级是由硬件排队线路或中断查询程序的查询顺序决定的,不可动态改变; 而中断优先级可以由中断屏蔽字来改变,反映的是正在处理的中断是否比新发生的中断的处理...
  • 中断机构和中断处理程序

    千次阅读 2018-12-29 18:06:32
    什么是中断? 中断实际上是对信号做出的一种反应, 即CPU对I/O设备发来的中断信号的一种反应。是由外部设备引起的。俗称外中断。 在此插一嘴什么是陷入?...中断处理程序位于I/O系统的底层,直接与硬件进行交...
  • 用户态和内核态 中断处理机制

    千次阅读 2021-01-15 22:57:54
    可以再响应其他中断 操作系统是性能攸关程序,且中断响应处理硬件要求,考虑系统效率和实现代价问题,中断的嵌套处理应限制在一定层数内,如3层 中断的嵌套处理改变中断处理次序,先响应的可能后处理 【多中断的...
  • 中断处理函数注意事项

    千次阅读 2020-05-09 11:49:24
    中断服务函数由硬件触发,因此不能获得参数,也无法返回值;另一方面,在中断服务函数中使用不可重入的函数,往往会导致问题。
  • 1、中断号定义 在stm32f10x.h中定义枚举类型IRQn中,定义了各个中断...CM3的中一个强大而方便的NVIC,它是属于Cortex内核的器件,中断向量表中60个中断都由它来处理。NVIC是Cortex-M3核心的一部分,关于它的资料...
  • x86中断处理过程

    千次阅读 2019-06-18 14:10:29
    文章目录了解x86中的中断源中断interrupts异常Exceptions了解CPU与操作系统如何处理中断x86如何通过中断处理来完成系统调用能够对中断向量表(中断描述表,简称IDT)进行初始化 了解x86中的中断源 中断、异常在具体...
  • Linux内核中断系统处理机制-详细分析

    万次阅读 多人点赞 2018-08-23 23:09:24
    日期 ...中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续...
  • 中断处理流程

    千次阅读 2018-10-25 17:19:01
    以s3c2440为例讲述,中断处理流程: 1.开中断。产生外部中断。流程如下按键-----&gt;中断控制器-------&gt;cpu(总开关) * 按键: 需要设置GPIO为中断模式,上升沿还是下降沿触发。 * 中断控制器: ...
  • 第7章中断处理程序

    千次阅读 2018-09-17 20:40:11
    产生中断的每个设备都一个相应的中断处理程序。例如,由一个函数专门处理来自系统时钟的中断,而另外一个函数专门处理由键盘产生的中断。一个设备的中断处理程序是它设备驱动程序的一部分——设备驱动程序是用于对...
  • 计算机组成原理(中断处理过程)

    千次阅读 多人点赞 2018-06-30 19:31:42
    中断处理过程 Created with Raphaël 2.1.2关中断保存断点,保存现场判别中断条件,转入中断服务程序开中断执行中断服务程序 关中断恢复现场,恢复断点开中断返回返回断点 关中断 进入不可再次响应中断的状态,...
  • 内核键盘中断处理进阶

    千次阅读 2016-10-09 20:55:37
    上一节,我们实现了键盘中断的响应,但响应的处理比较简单,只是向界面打印一条字符串而已,本节,我们将在屏幕上输出键盘中断更多的相关信息。当键盘上的一个按键按下时,键盘会发送一个中断信号给CPU,与此同时,...
  • 8086中断处理过程

    千次阅读 2020-04-09 09:25:53
    2)CPU在每执行一条指令的最后一个时钟周期时会采样INTR信号线判断有无新的中断请求,如果INTR标志为1(也就是有中断请求)CPU就会首先判断IF标志位,如果为1,CPU就允许中断响应。IF=0就忽略该中断。 3)IF=1,CPU...
  • 中断处理与进程调度的区别

    千次阅读 2020-12-19 21:36:23
    对于中断处理和进程调度的抢占方式(处理机调度),因为二者都打断的性质,都是抢占了CPU,所以容易混淆。 首先,中断处理是外设打断进程,比如一个进程在使用cpu,它的某条指令到达了中断周期,那么,这个进程被...
  • 中断和中断处理程序

    千次阅读 2015-04-09 21:28:59
    更好的办法是让硬件在需要的时候向内核发出信号(变内核主动为硬件主动),这就是中断机制。6.1 中断 硬件设备生成中断的时候并不考虑与处理器的时钟同步——也就是说中断随时可以产生。内核随时可能因为新到来的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 603,977
精华内容 241,590
关键字:

中断处理需要有中断

友情链接: C51jiekou.rar