Linux内核设计与实现 阅读笔记:7、中断和中断处理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jackailson/article/details/51100514

      By:Ailson Jack

      Date:2016.04.08

      个人博客:www.only2fire.com

      本文在我博客的地址是:http://www.only2fire.com/archives/62.html,排版更好,便于学习。

1、中断概述

      如何让处理器和外部设备协同工作,并且不会降低机器的整体性能呢?这里有两种方法:

      轮询是其中之一的解决办法,但是这种方法可能会让内核做不少的无用功。

      中断是另一种解决办法,即硬件在需要的时候向内核发出信号,此时内核会暂停当前正在处理的任务,去处理硬件发出的请求,待处理完成之后,接着执行之前的任务。

      中断分为同步中断(异常)和异步中断(一般由硬件产生)。

      硬件设备生成中断的时候并不考虑和处理器的时钟同步—换句话说就是中断随时可以产生。因此内核随时可能因为新到来的中断而被打断。不同的设备对应的中断不同,而每个中断都通过唯一的数字标志。这些中断值通常被称为中断请求(IRQ)线。每个IRQ线都会关联一个数值量。例如,在经典的PC机上,IRQ0是时钟中断,而IRQ1是键盘中断。

2、中断处理程序

扫描二维码关注公众号,回复: 3037623 查看本文章

     在响应一个特定中断的时候,内核会执行一个函数,这个函数就叫做中断处理程序(interrupt handler)或者中断服务例程(interrupt service routine,ISR)。

      一个设备的中断处理程序是它设备驱动程序的一部分—设备驱动程序是用于对设备进行管理的内核代码。

      中断随时可能发生,因此中断处理程序也就随时可能执行。所以中断处理程序必须尽可能快的被执行完,同时中断打断了系统其它任务的执行,如果中断处理程序运行的时间过长,那么系统的整体性能也就会下降。

      下面来看一个例子,假设某个中断需要处理的工作量比较大,比如网卡的中断处理程序,它在接收到中断之后,除了对硬件做出应答,还需要把硬件的网络数据包拷贝到内存,然后对其进行处理再交给合适的协议栈或应用程序。显然,这种工作量不会太小。

      为了解决中断处理程序既想运行得快,又想完成的工作量多的需求,我们一般中断处理分成两个部分或两半。中断处理程序是上半部(top half)—接收到一个中断,它就立即开始执行,但只做有严格时限的工作,例如对接收的中断进行应答或者复位硬件,这些工作都是在所有中断被禁止的情况下完成的。能够被允许稍后完成的工作会被推迟到下半部(bottom half)去,此后,在合适的时机,下半部会被开中断执行。这里只关心中断的上半部也就是中断的处理程序,而对于中断的下半部将在第8章中进行说明。

3、中断处理相关函数

1)、注册中断处理程序

      中断处理程序是管理硬件的驱动程序的一部分。驱动程序可以通过request_irq()函数注册一个中断处理程序(它被声明在文件<linux/interrupt.h>),并且激活给定的中断线,以处理中断,函数的定义如下:

/*

 * irq                   – 表示要分配的中断号

 * handler – 实际的中断处理程序

 * flags      – 标志位,表示此中断的具有特性

 * name    – 中断设备名称的ASCII 表示,这些会被/proc/irq和/proc/interrupts文件使用

 * dev        – 用于共享中断线,多个中断程序共享一个中断线时(共用一个中断号),依靠dev来区别各个中断程序

 * 返回值:

 * 执行成功:0

 * 执行失败:非0

 */

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

其中flags常用的标志:

IRQF_DISABLED:该标志被设置后,意味着内核在处理中断处理程序本身期间,要禁止所有的其它中断。如果不设置,中断处理程序可以与除本身外的其它任何中断同时运行。

IRQF_TIMER:该标志是特别为系统定时器的中断处理而准备的。

IRQF_SHARED:此标志表明可以在多个中断处理程序之间共享中断线。在同一个给定线上注册的每个处理程序必须指定这个标志;否则,在每条线上只能有一个处理程序。

      注意:request_irq()函数可能会睡眠,因此,不能在中断上下文或者其他不允许阻塞的代码中调用该函数。还有一点很重要,初始化硬件和注册中断处理程序的顺序必须正确,以防止中断处理程序在设备初始化完成之前就开始执行。

2)、释放中断处理程序

      卸载驱动程序时,需要注销相应的中断处理程序,并释放中断线,此时需要调用函数:

void free_irq(unsigned int irq, void *dev)

      如果指定的中断线不是共享的,那么,该函数删除处理程序的同时将禁用这条中断线。如果中断线是共享的,那么仅删除dev所对应的处理程序,而这条中断线本身只有在删除了最后一个处理程序时才会被禁用。必须在进程上下文中调用free_irq()。

3)、中断处理程序的声明

/*

 * 中断处理程序的声明

 * @irq               – 中断处理程序(即request_irq()中handler)关联的中断号

 * @dev    – 与 request_irq()中的dev一样,表示一个设备的结构体

 * 返回值:

 * irqreturn_t –  执行成功:IRQ_HANDLED  执行失败:IRQ_NONE

 */

static irqreturn_t intr_handler(int, irq, void *dev)

4、中断处理机制

中断处理的过程主要涉及3个函数:

      do_IRQ() — 与体系结构有关,对所接收的中断进行应答

      handle_IRQ_event() — 调用中断线上所有中断处理

      ret_from_intr() — 恢复寄存器,将内核恢复到中断前的状态

       中断从硬件到内核的路由如下图:

5、中断控制

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

      常见的控制方法见下表:

6、总结

      中断处理程序对处理时间的要求很高,如果一个中断要花费较长时间,那么中断处理一般分为2部分:

上半部只做一些必要的工作后,立即通知硬件继续自己的工作。

      中断处理中耗时的部分,也就是下半部的工作,CPU会在适当的时候去完成。

      注:转载请注明出处,谢谢!^_^

猜你喜欢

转载自blog.csdn.net/jackailson/article/details/51100514
今日推荐