Linux 的中断

中断概念和分类

中断是指 CPU 在执行程序的过程中,出现了某些突发事件急待处理, CPU 必须暂停当前程序的执行,转去处理突发事件,处理完毕后又返回原程序被中断的位置继续执行。

  • 根据中断的来源,中断可分为内部中断和外部中断,内部中断的中断源来自 CPU 内部(软件中断指令、溢出、除法错误等,例如,操作系统从用户态切换到内核态需借助 CPU 内部的软件中断),外部中断的中断源来自 CPU 外部,由外设提出请求。
  • 根据中断是否可以屏蔽,中断可分为可屏蔽中断与不可屏蔽中断( NMI ),可屏蔽中断可以通过设置中断控制器寄存器等方法被屏蔽,屏蔽后,该中断不再得到响应,而不可屏蔽中断不能被屏蔽。
  • 根据中断入口跳转方法的不同,中断可分为向量中断和非向量中断。采用向量中断的 CPU 通常为不同的中断分配不同的中断号,当检测到某中断号的中断到来后,就自动跳转到与该中断号对应的地址执行。不同中断号的中断有不同的入口地址。非向量中断的多个中断共享一个入口地址,进入该入口地址后,再通过软件判断中断标志来识别具体是哪个中断。

在 ARM 多核处理器里最常用的中断控制器是 GIC ( Generic Interrupt Controller ),如下图所示,它支持3种类型的中断。
ARM 中断控制器是 GIC

  • SGI ( Software Generated Interrupt ):软件产生的中断,可以用于多核的核间通信。一个 CPU 可以通过写 GIC 的寄存器给另外一个 CPU 产生中断。多核调度用的 IPI WAKEUP IPI_TIMER 、 IPI_RESCHEDULE 、 IPI_CALL_FUNC 、 IPI_CALL_FUNC_SINGLE 、 IPI_CPU_STOP 、 IPI_IRQ_WORK 、 IPI_COMPLETION 都是由 SGI 产生的。
  • PPI ( Private Peripheral Interrupt ):某个 CPU 私有外设的中断,这类外设的中断只能发给绑定的那个 CPU 。
  • SPI ( Shared Peripheral Interrupt ):共享外设的中断,这类外设的中断可以路由到任何一个 CPU 。

对于 SPI 类型的中断,内核可以通过如下 API 设定中断触发的 CPU 核:

extern int irq_set_affinity ( unsigned int irg , const struct cpumask * m );

在 ARM Linux 默认情况下,中断都是在CPU0上产生的,比如,我们可以通过如下代的把中断 irq 设定到 CPUi 上去:

irq_set_affinity ( irq , cpumask_of ( i ));

Linux 中断处理程序架构

设备的中断会打断内核进程中的正常调度和运行,系统对更高春吐率的迫求势必要求中服务程序尽址短小精悍。
但是,这个良好的愿望往往与现实并不吻合。在大多数真实的系统中。当中断到来时。要完成的工作往往并不会是短小的,它可能要进行较大量的耗时处理。

下图描述了 Linux 内核的中断处理机制。为了在中断执行时间尽量短和中斯处理需完成的工作尽量大之间找到一个平衡点, Linux 将中断处理程序分解为两个半部:顶半部( Top Half )和底半部( Botom Half )
Linux 中断处理机制

顶半部用于完成尽量少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态,并在清除中断标志后就进行“登记中断"的工作。“登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。
这样,顶半部执行的速度就会很快,从而可以服务更多的中断请求。

现在,中断处理工作的重心就落在了底半部的头上,需用它来完成中断事件的绝大多数任务。底半部几乎做了中断处理程序所有的事情、而且可以被新的中断打断,这也是底半部和顶半部的最大不同。
因为顶半部往往被设计成不可中断。底半部相对来说并不是非常紧急的,而且相对比较耗时,不在硬件中断服务程序中执行。

尽管顶半部、底半部的结合能够改善系统的响应能力,但是,僵化地认为 Linux 设备驱动中的中断处理一定要分两个半部则是不对的。
如果中断要处理的工作本身很少,则完全可以直接在顶半部全部完成。

其他操作系统中对中断的处理也采用了类似于 Linux 的方法,真正的硬件中断服务程序都应该尽量短。
因此,许多操作系統都提供了中断上下文和非中断上下文相结合的机制,将中断的耗时作保留到韭中断上下文去执行。
例如,在 VxWorks 中,网络设备包接收中断到来后,中断服务程序会通过netJobAdd 函数将耗时的包接收和上传工作交给 tNetTask 任务去执行。

在Linux,可以用如下节点查看系统中断统计信息:
系统中断统计信息 /proc/interrrupts

Linux 中断编程

申请和释放中断

在 Linux 设备驱动中,使用中断的设备需要申请和释放对应的中断,并分别使用内核提供的 request irq 和 free_irq 函数。

1.申请 irq

int request_irq ( unsigned int irq , irq_handler_t handler , unsigned long flags , const char * name , void * dev ); 
  • irq 是要申请的硬件中断号。
  • handler 是向系统登记的中断处理函数(顶半部) 是一个回调函数,中断发生时,系统调用这个函数, dev 参数将被传递给它。
  • irqflags 是中断处理的属性,可以指定中断的触发方式以及处理方式。
  1. 在触发方式方面,可以是 IRQF_TRIGGER_RISNG 、 IRQF_TRIGGER_FALLING 、 IRQF_TRIGGER_HIGH 、 IRQF_TRIGGER_LOW 等。
  2. 在处理方式方面,若设置了 IRQF SHARED ,则表示多个设备共享中断、 dev 是要传递给中断服务程序的私有数据,一般设置为这个设备的设备结构体或NULL
  • request irq 返回0表示成功,
  • 返回-EINVAL 表示中断号无效或处理函数指针为 NULL ,
  • 返回-EBUSY 表示中断已经被占用且不能共享。
int<
  • 8
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值