Linux下的中断与异常

基本概念

中断由硬件产生, 异常由软件产生. 有时我们也称呼它们为异步中断和同步中断. 我们使用中断来和外设之间通信, 使用异常来实现系统调用, 软中断等功能.

中断处理程序被称作interrupt request handle或者interrupt serviceroutine(ISR). 一个设备的中断处理程序是它的驱动的一部分.

大部分设备中断由do_IRQ()函数开始. 然后根据中断号分发到各自的中断处理函数中.

中断栈是在2.6的某个版本中被加入内核的, 之前中断处理程序会占用进程的内核栈. 不过不管怎么样, 我们在中断处理函数中还是要注意内存空间的使用.

 

经典问题

中断处理函数中为什么不能睡眠?

关于这方面, 网上主要的说法是由于中断处理函数运行于中断上下文中. 这确实是主要原因, 因为无论睡眠还是阻塞, 都是通过schedule()来实现的, 最终都要调用调度器提供的接口, 而调度器是基于进程上下文来调度的. (个人理解: 或许可以通过修改调度器的代码来支持被中断的线程的调度. 但是这样的话就要保存CPU的中断上下文以便恢复, 这样的设计实在丑陋). 其实通过最近的很多查阅和学习之后, 我觉得没有必要在这个问题上深究了, 最合理的解释反而是’设计如此’.(内核源码中把中断设计成快速执行和返回).

通过上面的描述我们发现, 要解决中断处理函数中睡眠的问题, 就必须把中断处理的过程线程化). 其实Ingo Molnar的linux实时性补丁中就是这么做的(参见http://www.ibm.com/developerworks/cn/linux/l-lrt/part2/). 目前linux的实时性补丁已经被合入到了linux 3.0以上版本的代码中去了.

在Linux标准内核中, 中断是最高优先级的执行单元, 不管内核当时处理什么, 只要有中断事件, 系统将立即响应该事件并执行相应的中断处理代码, 除非当时中断关闭(使用local_irq_disable失效了IRQ).

 

软中断处理函数中能不能睡眠?

软中断通常作为中断处理函数的底半部来使用, 被中断处理函数触发执行.

软中断有两种运行环境: 一是在中断处理函数返回时, 会执行raise_softirq()来检测是否存在需要执行的软中断, 此时软中断运行在中断上下文中. 还有一种情况是软中断由内核线程ksoftirqd调用, 此时软中断运行在进程上下文中. 但是由于不确定到底是在哪里被执行, 故在软中断中也是不能睡眠的. Tasklet也是一种软中断, 只是另外做了一层包装.

 

为什么说linux不是一个实时操作系统?

1.      中断无法抢占, 中断优先级比线程优先级永远都要高.

2.      Linux的调度器并非为了实时性而设计而是为了桌面性能. 在linux系统下, 线程优先级是动态的, 也就是就算高优先级的线程任务没完成, 低优先级的线程还是能够得到时间片.

3.      Linux内核代码在很多地方关闭了中断或者抢占(如中断处理或者简单的spin_lock), 这使得’实时性’得不到保障(高优先级线程有时候还是要等待低优先级线程完成任务).

4.      Linux的实时性补丁对上面的问题都做了改进(如中断线程化, spin_lock改成信号量从而增加抢占点), 但是也只实现了软实时, 真正的硬实时不光对软件有要求, 还对硬件有要求(比如必须支持硬件高精度计时). 不过我们可以看到, 实时性补丁已经完成了大部分改进了, 如果真的有这方面的需求的完, 完全可以在此基础上自己来修改.

 

为什么中断处理函数不需要考虑可重入性?

在中断处理函数还在执行的时候, 对应的中断线是被屏蔽的, 所以中断处理函数是可以不用考虑可重入性的, 这大大简化了我们的开发工作. 原文如下:

Reentrancy andInterrupt Handlers

Interrupthandlers in linux need not be reentrant. When a given interrupt handler isexecuting,the corresponding interruupt line is masked out on all processors,preventing another interrupt on the same line from being received, Normally allother interrupts are enabled,so other interrupts are serviced,but the currentline is always disabled. Consequently, the same interrupt interrupt hander isnerver invoked concurrently to service a nested interrupt. This greatlysimplifies writing your interruupt handler

 

我们看到linux内核源码中在很多地方(如常用的spin_lock)关闭了中断之后还去禁止抢占, 为什么?

其实这主要还是一个编程习惯的问题. 关闭中断的话, 内核主动性的抢占已经不可能发生了(因为内核抢占都是发生在中断返回之前). 但是如果当前线程调用了一些接口, 而接口里面又依次调用了preempt_disable(), preempt_enable()的话, 就被主动出让cpu. 显然我们要避免这样的错误发生, 最保险的就是在调用local_irq_save()之后, 再调用一次preempt_disable(). Linux文档中对此有专门的描述, 原文如下:

Note thatyou do not need to explicitly prevent preemption if you are holding
any locks or interrupts are disabled, sincepreemption is implicitly disabled
in those cases.

But keep in mind that 'irqs disabled' is afundamentally unsafe way of
disabling preemption - any spin_unlock()decreasing the preemption count
to 0 might trigger a reschedule. A simpleprintk() might trigger a reschedule.
So use this implicit preemption-disablingproperty only if you know that the
affected codepath does not do any of this.Best policy is to use this only for
small, atomic code that you wrote and whichcalls no complex functions.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值