中断

中断分为同步中断和异常中断。

同步中断(也称为异常)是当指令执行时由CPU控制单元产生的,之所以称为同步,是因为只有在一条指令终止后CPU才会发出中断。

异步中断是由其他硬件设备依照CPU时钟信号随机产生的。


中断处理与进程切换有一个明显差异。由中断或异常处理程序执行的代码不是一个进程,更确切地说,它是一个内核控制路径,代表中断发生时正在运行的进程执行。


每个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线,所有现有的IRQ线都与一个名为可编程中断控制器(PIC)的硬件电路的输入引脚相连,可编程中断控制器执行下列动作:

1. 监视IRQ线,检查产生的信号。如果有两条或两条以上的IRQ线上产生信号,就选择引脚编号较少的IRQ线。

2. 如果一个引发信号出现在IRQ线上

   a. 把接收到的引发信号转换成对应的向量

   b. 把这个向量存放中断控制器的一个I/O端口,从而允许CPU通过数据总线读此向量

   c. 把引发信号送到处理器的INTR引脚,即产生一个中断

   d. 等待,直到CPU通过把这个中断信号写进可编程中断控制器的一个I/O。端口来确认它,当这种情况发生时,清INTR线。

3. 返回第1步。


中断类型:I/O类型、时钟类型(本地APIC时钟或外部时钟)、处理器中断。


不管引起中断的电路种类如何,所有的I/O中断处理程序都执行四个相同的基本操作:

1. 在内核态堆栈中保存IRQ的值和寄存器的内容。

2. 为正在给IRQ线服务的PIC发送一个应答,这将允许PIC进一步发出中断。

3. 执行共享这个IRQ的所有设备的中断服务例程(ISR)

4. 跳到ret_from_intr()的地址后终止。


硬件中断号、软件中断号

硬件中断号是当中断发生时,处理器从外部的PIC上读到的数值。在Linux系统初始化期间,通过对PIC的配置,可以将硬件的连接转化成相应的硬件中断号,例如,8根外设中断线IRQ0~IRQ7,可以通过PIC的配置,分别映射成硬件中断号10~17,那么当IRQ0上有中断请求时,处理器通过读取PIC,就得到所对应的硬件中断号为10。

但驱动程序安装它们的中断处理程序时,是基于软件中断号的,也就是内核函数reqest-irq中的参数irq是个软件中断号。

软件中断号是Linux下的概念,为了支持多平台的关系,Linux不直接用硬件中断号索引irq_desc。所以当外部中断产生时,在外部中断入口函数中会通过读取PIC来获得本次中断的硬件中断号,获得硬件中断后,会将其映射成对应的软件中断号来索引irq_desc数组,从而获得该软件中断号上的中断处理函数。显然,这种硬件中断号到软件中断号的映射关系,应该在设备可以处理中断前就要建立好了,在Linux PowerPC中,这种映射关系是由函数irq_parse_and_map来完成的。


与中断相关的主要数据结构struct irq_desc,这个结构体用来描述中断源,是用来连接硬件中断和驱动程序中通过reqest_irq注册的中断处理函数之间的桥梁。

struct irq_desc主要成员:

struct irqcation *action;  //是一个指向由irqaction结构体组成的一个单向链表的头的指针。若该IRQ只被一个中断源使用,那么该链表的长度就是1,当有多个设备共享一个中断源时,该链表就会由多个irqaction结构体组成。

unsigned int depth;   //如果IRQ线被激活,则显示0;如果IRQ线被禁止了不止一次,则显示一个正数。

unsigned int status;  //描述IRQ线状态的一组标志。

struct irq_chip *chip;  //是一个指向hw_interrupt_type(或者irq_chip)的指针,其中定义的函数是平台相关的(更具体的,是中断控制器相关的),很显然,不同平台的中断控制器拥有不同的操作函数,比如enable、disable、mask、unmask某个中断的操作。从平台移植的角度,chip屏蔽了底层硬件的不同,使得在内核中某些代码成了平台无关性。但是对于不同平台的Linux,必须由BSP部分负责初始化irq_desc中的chip变量。


每次调用disable_irq()或disable_irq_nosyn()函数,depth字段的值增加,如果depth等于0,函数禁用IRQ线并设置它的IRQ_DISABLE标志。相反,每当调用enable_irq()函数,depth字段的值减少,如果depth变为0,函数激活IRQ线并清除IRQ_DISABLED标志。


struct irqaction主要成员

irq_handler_t handler;   //该指针所指向的函数就是中断服务程序

name;    //产生中断的硬件的名字

next;    //指向irqaction描述符表的下一个元素

irq;   //软件中断号

数组 

irq_desc irq_desc[NR_IRQS];       //NR_IRQS = 255;

中每一项都对应一个相应的中断源。

int reqest_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)


中断处理需要注意:

1. 在中断程序中,不能调用任何可能引起任务切换的函数,如TaskDelay、打印、P操作等。

2. 中断中做的事要尽可能少,做的越多出错的可能性就越大。一般来说,中断只需要做一个消息,传递就行了,把更多的处理留给任务去做。

3. 中断中使用的全局变量,在使用时必须使用关中断处理,不能用普通的信号量保护。


你可能感兴趣的:(计算机原理)