精华内容
下载资源
问答
  • 中断处理
    千次阅读
    2022-03-20 11:07:21

    3、中断处理:中断处理程序是被内核调用来响应中断的,它运行在中断上下文。

                           补充:    对Linux内核中进程上下文和中断上下文的理解

              内核空间和用户空间是操作系统理论的基础之一,即内核功能模块运行在内核空间,而应用程序运行 在用户空间。现代的CPU都具有不同的操作模式,代表不同的级别,不同的级别具有不同的功能,在较低 的级别中将禁止某些操作。Linux系统设计时利用了这种硬件特性,使用了两个级别,最高级别和最低级 别,内核运行在最高级别(内核态),这个级别可以进行所有操作,而应用程序运行在较低级别(用户态), 在这个级别,处理器控制着对硬件的直接访问以及对内存的非授权访问。内核态和用户态有自己的内存映 射,即自己的地址空间。 正是有了不同运行状态的划分,才有了上下文的概念。用户空间的应用程序,如果想要请求系统服务, 比如操作一个物理设备,或者映射一段设备空间的地址到用户空间,就必须通过系统调用来(操作系统提 供给用户空间的接口函数)实现。如下图所示:

              

               通过系统调用,用户空间的应用程序就会进入内核空间,由内核代表该进程运行于内核空间,这就涉 及到上下文的切换,用户空间和内核空间具有不同的地址映射,通用或专用的寄存器组,而用户空间的进 程要传递很多变量、参数给内核,内核也要保存用户进程的一些寄存器、变量等,以便系统调用结束后回 到用户空间继续执行,所谓的进程上下文,就是一个进程在执行的时候,CPU的所有寄存器中的值、进程 的状态以及堆栈中的内容,当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当 前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。

              同理,硬件通过触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变 量和参数也要传递给内核,内核通过这些参数进行中断处理,中断上下文就可以理解为硬件传递过来的这 些参数和内核需要保存的一些环境,主要是被中断的进程的环境。   Linux内核工作在进程上下文或者中断上下文。

                 提供系统调用服务的内核代码代表发起系统调用的应 用程序运行在进程上下文;另一方面,中断处理程序,异步运行在中断上下文。中断上下文和特定进程无 关。

                运行在进程上下文的内核代码是可以被抢占的(Linux2.6支持抢占)。但是一个中断上下文,通常都 会始终占有CPU(当然中断可以嵌套,但我们一般不这样做),不可以被打断。正因为如此,运行在中断 上下文的代码就要受一些限制,不能做下面的事情:

      1)、睡眠或者放弃CPU。

      这样做的后果是灾难性的,因为内核在进入中断之前会关闭进程调度,一旦睡眠或者放弃CPU,这时

    内核无法调度别的进程来执行,系统就会死掉

      2)、尝试获得信号量

      如果获得不到信号量,代码就会睡眠,会产生和上面相同的情况

      3)、执行耗时的任务

      中断处理应该尽可能快,因为内核要响应大量服务和请求,中断上下文占用CPU时间太长会严重影响

    系统功能。

      4)、访问用户空间的虚拟地址

      因为中断上下文是和特定进程无关的,它是内核代表硬件运行在内核空间,所以在终端上下文无法访

    问用户空间的虚拟地址 

    小结:进程上下文就是应用程序那边向内核发来的中断信号;而中断上下文就是硬件向内核发来的终端信号,前者可以被抢占,后者一般不能够被抢占。

     

    更多相关内容
  • 中断和中断处理程序1 中断2 中断处理程序3 注册中断处理程序4 编写中断处理程序共享的中断处理程序中断处理程序实例5 中断上下文6 中断处理机制的实现7 中断控制禁止和激活中断禁止指定中断线中断系统的状态 ...

    Linux内核要对连接到计算机上的所有硬件设备进行管理,要与它们进行通信。但是,处理器的速度跟外围硬件设备的速度往往不是一个数量级的,硬件的响应很慢,内核应该在此期间处理其他事物,等到硬件真正完成了请求的操作之后,再回过头来对它进行处理。中断机制让硬件在需要的时候再向内核发出信号,内核来处理硬件的请求。

    1 中断

    中断使得硬件得以与处理器进行通信。硬件设备生成中断的时候并不考虑与处理器的时钟同步,换句话说,中断随时都可以产生,因此,内核随时可能因为新到来的中断而被打断。

    从物理学的角度看,中断是一种电信号,由硬件设备生成,并直接送入中断控制器的输入引脚上,然后再由中断控制器向处理器发送相应的信号。处理器一经检测到此信号,便中断自己的当前工作转而处理中断。此后,处理器会通知操作系统已经产生中断,这样,操作系统就可以对这个中断进行适当的处理了。

    不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识,这些中断值通常被称为中断请求(IRQ)线。特定的中断总是与特定的设备相关联,并且内核要知道这些消息。

    异常

    异常与中断不同,它在产生时必须考虑与处理器时钟同步。异常也常称为同步中断。在处理器执行到由于编程失误而导致的错误指令的时候,或者是在执行期间出现特殊情况(例如缺页),必须靠内核来处理的时候,处理器就会产生一个异常。

    2 中断处理程序

    在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序或中断服务例程(interrupt service routine,ISR)。产生中断的每个设备都有一个相应的中断处理程序。中断处理程序通常不是和特定设备关联,而是和特定中断关联的,也就是说,如果一个设备可以产生多种不同的中断,那么该设备就可以对应多个中断处理程序,相应的,该设备的驱动程序也就需要准备多个这样的函数。

    在Linux中,中断处理程序看起来就是普普通通的C函数,只不过这些函数必须按照特定的类型声明,以便内核能够以标准的方式传递程序的信息。中断处理程序与其他内核函数的真正区别在于:中断处理程序是被内核调用来响应中断的,而它们运行在我们称之为中断上下文的特殊上下文中。

    上半部与下半部的对比

    一般把中断处理切为两个部分,中断处理程序是上半部,接受到一个中断,它就立即开始执行,但只做有严格时限的工作,例如对接受的中断进行应答或复位硬件,这些工作都是在所有中断被禁止的情况下完成的。能够被允许稍后完成的工作会被推迟到下半部去。此后,在合适的时机,下半部就开中断执行,Linux提供了实现下半部的各种机制,下一篇文章会讨论,现在我们要记住上半部是中断处理程序,只做有严格时限的工作。

    3 注册中断处理程序

    中断处理程序是驱动程序的组成部分。每一设备都有相关的驱动程序,如果设备使用中断,那么相应的驱动程序就注册一个中断处理程序。

    驱动程序可以通过下面的函数注册并激活一个中断处理程序:

    /* request_irq分配一个中断线 */
    int request_irq(unsigned int irq,
    				irqreturn_t (*handler)(int,void *,struct pt_regs *),
    				unsigned long irqflags,
    				const char * devname,
    				void *dev_id)
    

    第一个参数irq表示要分配的中断号。对某些设备,如传统PC设备上的系统时钟或键盘,这个值通常是预先定死的。而对于大多数其他设备,这个值要么是可以通过探测获取,要么可以通过编程动态确定。

    第二个参数handle是实际的中断处理程序。只要操作系统一接收到该中断,该函数就被调用。

    第三个参数irqflags可以为0,也可能是下列一个或多个标志的位掩码:

    • SA_INTERRUPT:此标志表明给定的中断处理程序是一个快速中断处理程序。在本地处理器上,快速中断处理程序在禁止所有中断的情况下运行
    • SA_SAMPLE_RANDOM:此标志表明这个设备产生的中断对内核熵池有贡献。内核熵池负责提供从各种随机事件导出的真正的随机数。
    • SA_SHIRQ:此标志表明可以在多个中断处理程序之间共享中断线。

    第四个参数devname是给中断相关的设备起的名字。这些名字会被/proc/irq和/proc/Interrupt文件使用,以便与用户通信。

    第五个参数dev_id主要用于共享中断线。如果无需共享中断线,那么将该参数赋值为NULL就可以了,但是,如果中断线是被共享的,那么必须传递唯一的信息,用来区分用的是共享中断线上的那个中断处理程序。实践中往往会通过它传递驱动程序的设备结构。

    request_irq()函数可能会睡眠,因此,不能在中断上下文或其他不允许阻塞的代码中用该函数,至于request_irq()会睡眠,那是在注册的过程中,内核需要在/proc/irq文件创建一个中断对应的项,会调用kmalloc()来请求分配内存,函数kmalloc()是可以睡眠的。

    在一个驱动程序中请求一个中断线,并在通过request_irq()注册中断处理程序:

    if(request_irq(irqn,my_interrupt,SA_SHIRQ,"my_device",dev))
    {
    	printk(KERN_ERR "my_device:connot register IRQ %d \n",irqn);
    	return -RIO;
    }
    

    irqn是请求的中断线,my_interrupt是中断处理程序,中断线可以共享,设备名为"my_device",而且我们通过dev_id传递dev结构体。

    释放中断处理程序

    卸载驱动程序时,需要注销相应的中断处理程序,并释放中断线。可以调用void free_irq(unsigned int irq,void *dev_id)来释放中断线。
    如果指定的中断线不是共享的,那么,该函数删除中断处理程序的同时将禁用这条中断线。如果中断线是共享的,则仅删除dev_id所对应的处理程序,而这条中断线本身只有在删除了最后一个程序程序时才会被禁用。

    4 编写中断处理程序

    以下是一个典型的中断处理程序声明:

    static irqreturn_t intr_handler(int irq,void *dev_id,struct pt_regs *regs);
    

    它的类型与request_irq()参数中handler所要求的参数类型相匹配。第一个参数irq就是这个中断处理程序要响应的中断的中断线号。第二个参数dev_id是用一个通用指针,它与中断处理程序注册时传递给request_irq()的采纳数dev_id必须一致,可以用来区分共享同一个中断处理程序的多个设备,最后一个参数regs是一个指向结构的指针,该结构包含处理中断之前处理器的寄存器和状态。除了调试的时候,它们很少使用到。

    中断处理程序的返回值是一个特殊类型:irqreturn_t。中断处理程序可能返回两个特殊的值,IRQ_NONE和IRQ_HANDLED。当中断处理程序检测到一个中断,但该中断对应的设备并不是在注册处理函数期间指定的产生源时,返回IRQ_NONE;当中断处理程序被正确调用,且确实是它所对应的设备产生的中断,返回IRQ_HANDLED。

    重入和中断处理程序

    Linux中的中断处理程序是无需重入的。当一个给定的中断处理程序在执行时,相应的中断线在所有处理器上都会被屏蔽掉,以防止在同一中断线上接受到另一个新的中断。

    共享的中断处理程序

    共享中断线的处理程序和非共享中断线的处理程序在注册和运行方式上比较相似,但主要有以下差异:

    • request_irq()的flags参数必须设置SA_SHIRQ标志
    • 对每个注册的中断处理程序来说,dev_id必须是唯一的。指向任意设备结构的指针就可以满足这一要求,通常会选择设备结构,因为它是唯一的,而且中断处理程序可能会用到它。不能共享就传NULL
    • 中断处理程序必须能区分它的设备是否真的产生了中断,

    中断处理程序实例

    让我们考察一个实际的中断处理程序,它来自RTC(real_time clock)驱动程序,可以在drivers/char/rtc.c中找到。RTC驱动程序装载时,rtc_init()函数会被调用,对这个驱动程序进行初始化。它的职责之一就是注册中断处理程序:

    /*
    	 * XXX Interrupt pin #7 in Espresso is shared between RTC and
    	 * PCI Slot 2 INTA# (and some INTx# in Slot 1). SA_INTERRUPT here
    	 * is asking for trouble with add-on boards. Change to SA_SHIRQ.
    	 */
    	if (request_irq(rtc_irq, rtc_interrupt, SA_INTERRUPT, "rtc", (void *)&rtc_port)) {
    		/*
    		 * Standard way for sparc to print irq's is to use
    		 * __irq_itoa(). I think for EBus it's ok to use %d.
    		 */
    		printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);
    		return -EIO;
    	}
    

    中断线号由rtc_irq指定,rtc_interrupt是我们的中断处理程序,驱动程序的名称为rtc,因为这个设备不允许共享中断线,且处理程序没有用到什么特殊的值,因此给dev_id的值是NULL。

    5 中断上下文

    当执行一个中断处理程序或下半部时,内核处于中断上下文中。进程上下文是一种内核所处的操作模式,此时内核代表进程执行,在进程上下文中,可以通过current宏关联到当前进程,此外,因为进程是以进程上下文的形式连接到内核的,因此,在进程上下文可以睡眠,也可以调用调度程序。

    中断上下文和进程并没什么瓜葛。因为没有进程的背景,所有中断上下文不可睡眠

    中断处理程序栈的设置是一个配置选项。它们共享所属中断进程的内核栈。内核栈的大小是两页。因为这种设置,中断处理程序共享别人的堆栈,所以它们从栈中获取内容非常节省空间。

    6 中断处理机制的实现

    中断处理在linux中的实现非常依赖体系结构,实现依赖于处理器、所使用的中断控制器的类型、体系结构的设计以及机器本身。
    下图是中断从硬件到内核的步骤。
    在这里插入图片描述
    设备产生中断,通过总线把电信号发送给中断控制器。如果中断线是激活的,那么中断控制器就会把中断发往处理器。在大多数体系结构中,这个工作就是通过电信号给处理器的特定管脚发送一个信号。除非在处理器禁止该中断,否则,处理器会立即停止它正在做的事,关闭中断系统,然后跳到中断处理程序的入口点去运行。

    对于每条中断线,处理器都会跳到对应的唯一的位置。这样,内核就可知道所接收中断的IRQ号了。入口点只是在栈中保存这个号,并存当前寄存器的值;然后,内核调用函数do_IRQ()。

    do_IRQ()的声明如下:

    unsigned int do_IRQ(struct pt_regs regs);
    

    因为C的调用惯例是要把函数参数放在栈的顶部,因此pt_regs结构包含原始寄存器的值,这些值是以前在汇编入口例程中保存在栈的的,中断的值也会得到保存,所以,do_IRQ()可以将它提取出来。

    计算出中断号后,do_IRQ()对所接收的中断进行应答,禁止这条线上的中断传递。do_IRQ()需要确保在这条中断线上有一个有效的处理程序,而且这个程序已经启动但是当前没有执行。如果是这样的话,do_IRQ()就调用handle_IRQ_event()来运行为这条中断线所安装的中断处理程序,在kernel/irq/handle.c文件中

    /*
     * Have got an event to handle:
     */
    fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
    				struct irqaction *action)
    {
    	int ret, retval = 0, status = 0;
    
    	if (!(action->flags & SA_INTERRUPT))
    		local_irq_enable();
    
    	do {
    		ret = action->handler(irq, action->dev_id, regs);
    		if (ret == IRQ_HANDLED)
    			status |= action->flags;
    		retval |= ret;
    		action = action->next;
    	} while (action);
    
    	if (status & SA_SAMPLE_RANDOM)
    		add_interrupt_randomness(irq);
    	local_irq_disable();
    
    	return retval;
    }
    

    7 中断控制

    Linux内核提供了一组接口用于操作机器上的中断状态。这些接口为我们提供了禁止当前处理器的中断系统,或屏蔽掉整个机器的一条中断线的能力,这些接口是与体系有关的,可以在<asm/system.h>和<asm/irq.h>中找到。

    禁止和激活中断

    用于禁止当前处理器(仅仅是当前处理器)上的本地中断,随后又激活它们的语句是:

    local_irq_disable();
    local_irq_enable();
    

    这两个函数通常以单个汇编指令来实现(取决于体系结构)。实际上,在x86中,local_irq_disable()仅仅是cli指令,而local_irq_enable()只不过是sti指令。

    local_irq_disable是禁止当前处理器上的所有中断,local_irq_enable是开启当前处理器上的所有中断,即使在禁止前有些中断是关闭的,在调用local_irq_enable()后也会被激活。所以我们需要一种机制把中断恢复到以前的状态而不是简单地禁止或激活。在禁止之前保存当前的中断系统,激活时恢复就行。

    unsigned long flags;
    lcoal_irq_save(flags)
    local_irq_restore(flags);
    

    flags必须是unsigned long类型。local_irq_save () 是保存本地中断传递的当前状态,然后禁止本地中断传递。local_irq_restore () 是恢复本地中断到flags的状态。

    禁止指定中断线

    在某些情况下,只禁止整个系统中一条特定的中断线就够了。为此,Linux提供了四个接口:

    void disable_irq(unsigned int irq);
    void disable_irq_nosync(unsigned int irq);
    void enable_irq(unsigned int irq);
    void synchronize_irq(unsigned int irq);
    

    disable_irq和disable_irq_nosync禁止中断控制器上指定的中断线,即禁止给定中断向系统中所有处理器的传递。我们来看看disable_irq的具体实现:

    /**
     *	disable_irq - disable an irq and wait for completion
     *	@irq: Interrupt to disable
     *
     *	Disable the selected interrupt line.  Enables and disables
     *	are nested.  This functions waits for any pending IRQ
     *	handlers for this interrupt to complete before returning.
     *	If you use this function while holding a resource the IRQ
     *	handler may need you will deadlock.
     *
     *	This function may be called - with care - from IRQ context.
     */
    void disable_irq(unsigned int irq)
    {
    	struct irqdesc *desc = irq_desc + irq;
    
    	disable_irq_nosync(irq);
    	if (desc->action)
    		synchronize_irq(irq);
    }
    

    此函数在返回之前等待中断irq的任何挂起的 中断处理程序完成。disable_irq会阻塞,所以不能在在中断处理函数中使用,如果在中断处理程序中使用,会导致死锁,电脑会死机。

    disable_irq_nosync不会阻塞,会立即返回,可在中断处理函数中使用。

    enable_irq函数的参数是int型变量,代表操作中断对应的中断号

    函数synchronize_irq等待一个特定的中断处理程序的退出,所以该函数也会阻塞。

    这些函数的调用都可以嵌套,但要记住在一条指定的中断线上,对disable_irq()或disable_irq_nosync()的每次调用,都需要相应地调用一次enable_irq()。只有在对enable_irq()完成最后一次调用后,才真正重新激活了中断线。例如,如果disable_irq()被调用了两次,那么直达第二次调用enable_irq()后,才能真正地激活中断线。

    禁止多个中断处理程序共享的中断线是不合适的。禁止中断线也就禁止了这条线上所有设备的中断传递。因此,用于新设备的驱动程序应该尽量不使用这些接口。

    中断系统的状态

    宏irqs_disable()定义在<asm/system.h>中,如果本地处理器上的中断系统被禁止,则它返回非0,否则返回0.
    在<asm/hardirq.h>中定义了的两个宏提供了一个用来检查内核的当前上下文的接口,它们是:

    in_interrupt();
    in_irq();
    

    in_interrupt用来检测内核是否处于中断上下文中,如果是的话,返回非0。说明此刻内核正在执行中断处理程序,或者正在执行下半段处理程序,如果返回0,此时内核处于进程上下文中。宏in_irq只有在内核确实正在执行中断处理程序时才返回非0.

    8 总结

    中断就是由硬件打断操作系统。内核提供的接口包括注册和注销中断处理程序,禁止中断,屏蔽中断线,以及检查中断系统的状态。
    因为中断打断了其他代码的执行(进程,内核本身,甚至其他中断处理程序),它们必须赶快执行完。为了大量的工作与必须快速执行完之间求得平衡,内核把处理中断的工作分为两部分。中断处理程序,也就是上半部,下半部我们还没有讨论。

    展开全文
  • 中断和中断处理流程

    千次阅读 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重新开始执行。

    展开全文
  • 一种方式是使用轮训(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》

    展开全文
  • 中断处理程序:在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序。产生中断的每一个设备都有一个相应的中断处理程序。中断处理程序是被内核调用来响应中断的,他们运行于中断上下文中,该上下...
  • 系统调用和中断处理的异同

    千次阅读 2020-08-11 16:22:54
    Linux系统下MIPS体系结构为例。 1 trap_init(void) /* 系统初始化.../* except_vec3_generic 根据cause寄存器跳转到其若干类异常/中断处理函数中*/ 3 set_except_vector(0, rollback ? rollback_handle_int : handle_
  • 一个完整的中断处理过程应该包括

    千次阅读 2020-12-23 01:30:11
    一个完整的中断处理过程应该包括:中断请求、中断排队或中断判优、中断响应、中断处理和中断返回等环节,下面分别进行讨论。1.中断请求中断请求是由中断源向CPU发出中断请求信号。外部设备发出中断请求信号要具备...
  • 中断处理的一般过程

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

    千次阅读 2022-03-18 22:15:05
    一、关中断 处理器响应中断后,首先要保护程序的现场状态,在保护现场过程中, CPU 不应该响应更高级中断源的中断请求。否则 ,如果现场保存不完整,在中断服务程序结束后,也就不能正确地恢复并继续执行现行程序。...
  • 展开全部1)中断e69da5e6ba903231313335323631343130323136353331333431366338响应的事前准备:系统要想能够应对各种不同的中断信号,总的来看就是需要知道每种信号应该由哪个中断服务程序负责以及这些中断服务程序...
  • 用户态和内核态 中断处理机制

    千次阅读 2021-01-15 22:57:54
    决定中断处理次序的因素 中断屏蔽可以使中断装置不响应某些中断 中断优先级决定了中断装置响应中断的次序 中断可以嵌套处理,但嵌套的层数应有限制 中断的嵌套处理改变了中断处理的次序 【多重中断处理-1】 X、Y两...
  • 嵌入式RTOS---异常和中断处理流程

    千次阅读 2019-11-26 22:50:39
    中断处理分为统一的中断处理和独立的中断处理; 1.1 统一的异常和中断处理 1.1.1 ARM的异常模式 所谓异常,指的是中止了程序正常的执行过程而不得不完成一些特殊的工作(异常工作)。 中断也是一种异常,中断...
  • 中断处理的基本过程

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

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

    千次阅读 2018-12-05 21:02:51
    当异常中断处理程序使用了数据栈时,可以通过下面的指令在进入异常中断处理程序时保存被中断程序的执行现场,在退出异常中断处理程序时恢复被中断程序的执行现场。异常中断处理程序中使用的数据栈由用户提供。 ...
  • 第7章中断处理程序

    千次阅读 2018-09-17 20:40:11
    7.2 中断处理程序 在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序或中断服务例程。产生中断的每个设备都有一个相应的中断处理程序。例如,由一个函数专门处理来自系统时钟的中断,而另外一...
  • 中断响应优先级和中断处理优先级的区别 中断响应优先级是由硬件排队线路或中断查询程序的查询顺序决定的,不可动态改变; 而中断优先级可以由中断屏蔽字来改变,反映的是正在处理的中断是否比新发生的中断的处理...
  • ARM中断处理过程

    万次阅读 2018-05-08 15:18:57
    一、前言本文主要以ARM体系结构下的中断处理为例,讲述整个中断处理过程中的硬件行为和软件动作。具体整个处理过程分成三个步骤来描述:1、第二章描述了中断处理的准备过程2、第三章描述了当发生中的时候,ARM硬件的...
  • 中断处理过程

    万次阅读 2019-08-13 16:05:45
    概念 中断全过程指的是从中断源发出中断请求开始,CPU响应这个请求,现行程序被中断,转至中断服务程序,直到中断...大体上可以把中断全过程分为5个阶段:中断请求、中断判优、中断响应、中断处理和中断返回。 ...
  • 中断机构和中断处理程序

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

    千次阅读 2020-05-09 11:49:24
    中断服务函数由硬件触发,因此不能获得参数,也无法返回值;另一方面,在中断服务函数中使用不可重入的函数,往往会导致问题。
  • 中断处理与进程调度的区别

    千次阅读 2020-12-19 21:36:23
    中断处理与进程调度的区别 对于中断处理和进程调度的抢占方式(处理机调度),因为二者都有打断的性质,都是抢占了CPU,所以容易混淆。 首先,中断处理是外设打断进程,比如一个进程在使用cpu,它的某条指令到达了...
  • 1、中断号定义 在stm32f10x.h中定义枚举类型IRQn中,定义了各个中断...CM3的中有一个强大而方便的NVIC,它是属于Cortex内核的器件,中断向量表中60个中断都由它来处理。NVIC是Cortex-M3核心的一部分,关于它的资料...
  • 8086中断处理过程

    千次阅读 2020-04-09 09:25:53
    当外设向CPU发送可屏蔽中断请求: 1)8259中断控制器通过INTR信号线发送高电屏请求信号 2)CPU在每执行一条指令的最后一个时钟周期时会采样INTR信号线判断有无新的中断请求,如果INTR标志为1(也就是有中断请求)...
  • ARM中断处理流程

    千次阅读 2017-06-17 22:52:07
    这边文章主要是讲解一下,对ARM_Linux中断处理的一个流程介绍,在底层处理部分,不同的架构会有不一样处理,但Linux中断注册过程是一样的。   一、Linux中断的简易模型: 当一个IRQ产生时,会发生什么? 当一个...
  • 中断处理流程

    千次阅读 2018-10-25 17:19:01
    以s3c2440为例讲述,中断处理流程: 1.开中断。产生外部中断。流程如下按键-----&gt;中断控制器-------&gt;cpu(总开关) * 按键: 需要设置GPIO为中断模式,上升沿还是下降沿触发。 * 中断控制器: ...
  • 一、ZYNQ 的中断系统结构 ZYNQ中断结构图如下 二、 通用中断控制器(GIC,General Interrupt Controller) 2.2 CPU接口功能 三、GIC控制源: 软中断(SGI, Software Generated ...四、中断处理流程 五、ZYNQ的软中断设置
  • 一,中断的定义 二 ,中断处理的过程 三,8086/8088CPU可以处理256种不同类型的终端 四,中断服务程序的设计方法 五中断向量表的建立

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 662,414
精华内容 264,965
关键字:

中断处理

友情链接: digital-modulation.rar