linux内核设计与实现---中断和中断处理(7)

一、中断:终端是一种电信号,由硬件设备生成,并直接送入中断控制器的输入引脚中——终端控制其是一个简单的电子芯片,其作用是将多路中段管线,采用复用技术只通过一个和处理器相连接的管线与处理器通信。(异步)—由硬件产生的异步中断。
异常:与中断不同,它在产生时不需要考虑与处理器时钟同步。也称为同步中断。在处理器执行到由于编程失误而导致的指令错误(如除0)的时候,或者是执行期间出现特殊情况(如缺页),必须靠内核来处理的时候,处理器就会产生一个异常。—由处理器本身产生同步中断。(还有一种通过软中断实现系统调用,陷入内核—系统调用处理程序异常)

二、中断处理程序

  • 中断处理程序(interrupt handler)或中断服务例程(interrupt service routine, ISR)。
  • 一个设备的中断处理程序是它设备驱动程序的一部分—设备驱动程序是用于对设备进行管理的内核代码。
  • 中断上下文又称为原子上下文,该上下文的执行代码不可阻塞。

三、上半部与下半部

  • 上半部(top half)—接收到一个中断,它就立即开始执行,但只做有严格时限的工作,例如接受的中断进行应答或复位硬件。所有中断都被禁止的情况下完成。
  • 下半部(bottom half)—能够被允许稍后完成的工作会推迟到下半部。下半部开中断执行。

四、注册中断处理程序

  • 中断处理程序是管理硬件的驱动程序的组成部分。每一设备都有相关的驱动程序,如果设备使用中断,那么相应的驱动程序就注册一个中断处理程序。
    /* request_irq:分配一条给定的中断线*/
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
typedef irqreturn_t (*irq_handler_t)(int, void *)

    • 第一个参数 irq 是要分配的中断号。有些设备预先确定,大多数设备需要探测获取。
    • 第二个参数 handler 是指向中断处理程序的指针
  • 中断处理程序标志:
    • 第三个参数:flag可以为0,也可以是下列标志——IRQF_DISABLE--是否屏蔽掉其他中断(一般不会屏蔽), IRQF_SAMPLE_RANDOM,IRQF_TIMER,IRQF_SHARED---此标志表明可以在多个中断处理程序之间共享中断线。
    • 第四个参数name与中断相关的设备的ASCII文本表示。如键盘对应“keyboard”。
    • 第五个参数dev用于共享中断线。当一个中断处理程序需要释放(驱动需要卸载),dev将提供唯一的标识信息(cookie),以便从共享中断线的诸多中断处理程序中删除指定的那一个。内核每次调用中断处理程序时,都会把这个指针传递给它。实践中往往通过它传递驱动程序的设备结构:这个指针是唯一的,而且有可能在中断处理程序中被用到。
    • request_irq() 函数可能会睡眠,因此,不能在中断上下文或其他不允许阻塞的代码中调用该函数。在注册的过程中,内核需要在/proc/irq文件中创建一个与中断对应的项。函数proc_mkdir()创建procfs项。调用proc_create()进行设置,proc_create()调用kmalloc() 来分配内存,该函数是可以睡眠的。
    • 成功注册,函数值返回0。
    • 初始化硬件和注册中断处理程序的顺序必须正确,防止初始化完成之前,中断触发。
  • 释放中断(卸载驱动程序):void free_irq(unsigned int irq , void *dev) 如果中断线不是共享的,那么,该函数删除处理程序的同时将禁用中断线,否则,只删除dev所对应的处理程序,中断线只有在删除了最后一个处理程序后才会禁用。

五、中断处理程序

  • static irqreturn_t intr_handler(int irq,void *dev)
    • 第一个参数是中断号。如今没有太大用处,只能在打印日志信息时会用到。
    • 第二个参数dev是一个通用指针,它与request_irq()的dev必须一致。
    • 中断处理程序返回值是一个特殊类型:irqreturn_t(实际是int型)。返回值为:IRQ_NONE(不是注册时的中断设备),IRQ_HANDLED(正确调用)
    • 重入和中断处理程序:linux中的中断处理程序是无需重入的。当一个在给定的中断处理程序正在执行时,相应的中断线在所有处理器上都会被屏蔽掉,以防止在同一中断线上接受另一新的中断。但不同中断线上的其他中断都能被处理。同一中断处理程序绝对不会被同时调用以处理嵌套的中断。
  • 共享的中断处理程序
    • request_irq()的参数flags必须设置IRQF_SHARED标志。
    • dev参数必须是唯一的
    • 中断处理程序必须能够区分它的设备是否真的产生了中断。
    • 内核接受到一个中断后,它将调用在该中断线上注册的每一个处理程序。因此,一个处理程序必须知道它是否应该为这个中断负责。如果不是,便立即退出。需要硬件设备提供状态寄存器或类似机制。

六、中断上下文

  • 因为没有后备进程(不能被调度),中断上下文不可以睡眠。因此,不可以在中断上下文调用某些函数睡眠函数,这是中断上下文对函数有限制的。、
  • 中断上下文具有较为严格的时间限制,因为它打断了其他代码。记住:中断处理程序打断了其他代码(可能是打断了在其他中断线上的另一中断处理程序)。因为这种异步执行特性,所以有时间限制。
  • 中断栈:早期中断共用被中断的内核栈。2.6版后,增加一页中断栈(4kb),同时也减少了内核栈。

  • 禁止和激活中断

    • 禁止中断,可以确保某个中断处理程序不会抢占当前的代码
    • 禁止中断还可以禁止抢占。
      但这两种都没有提供任何保护机制来防止来自其他处理器的并发访问。
    • 需要获取锁来防止其他处理器对共享数据的并发访问。
    • 获取自旋锁的同时也伴随着禁止本地中断。
      锁提供的保护机制是防止来自其他处理器的并发访问,禁止中断提供的保护机制是防止来自其他中断处理程序的并发访问。
    • local_irq_disable/enable()
    • local_irq_save/restore(flags):flags 参数是以值传递。该参数包含具体体系结构数据,也就是包含中断西戎的状态。flags不能传递给另一个函数,因此这对函数只能在同一函数中进行。
  • 禁止指定中断线

    • 在某些情况下,只禁止整个系统中一条特定的中断线就够了。即屏蔽掉(masking out)一条中断线。
    • void disable_irq(unsigned int irq); void disable_irq_nosync(unsigned int irq);这两个函数是禁止中断控制器上指定的中断线,即禁止给定中断线向系统中所有的处理器的传递。前者必须保证在该函数返回之前该中断线上的中断处理程序全部执行完成。后者不会等待。
    • void synchronize_irq(unsigned int irq);等待一个特定的中断处理程序的退出,才会返回。
    • void enable_irq(unsigned int irq); 该函数在一条指定的中断线上必须与上边三个函数成对出现。
    • 这三个函数可以从中断或进程上线文中调用,而且不会睡眠。
  • 中断系统的状态

    • irq_disable()定义在< asm/system.h >中。如果本地处理器上的中断系统被禁止,则它返回非0;否则返回0。
    • in_interrupt();内核处于任何类型的中断处理中,它返回非0,说明内核此刻正在执行中断处理程序,或者正在执行下半部处理程序。in_irq();只有在内核确实正在执行中断处理程序时才返回非0。
    • 通常情况下,你要检查自己是否处于进程上下文中。确保自己不在中断上下文中,因为代码可能需要睡眠。

猜你喜欢

转载自blog.csdn.net/u012418573/article/details/77104043
今日推荐