ldd3学习之七:中断处理
来源:互联网 发布:多多返利网站源码 编辑:程序博客网 时间:2024/04/30 19:20
一个“中断”仅是一个信号,当硬件需要获得处理器对它的关注时,就可以发送这个信号。
1.安装中断处理例程
内核维护了一个中断信号线的注册表,类似于 I/O 端口的注册表。模块在使用中断前要先请求一个中断通道(或者IRQ中断请求),并在使用后释放它。所用的函数声明在
int
void
函数的参数如下:
中断处理例程可在驱动初始化时或在设备第一次打开时安装。推荐在设备第一次打开、硬件被告知产生中断前时申请中断,因为可以共享有限的中断资源。这样调用 free_irq的位置是设备最后一次被关闭、硬件被告知不用再中断处理器之后。但这种方式的缺点是必须为每个设备维护一个打开计数。
if
{
}
i386 和 x86_64 体系定义了一个函数来查询一个中断线是否可用:
int
快速和慢速处理例程
快速中断是那些能够很快处理的中断,而处理慢速中断会花费更长的时间。在处理慢速中断时处理器重新使能中断,避免快速中断被延时过长。在现代内核中,快速和慢速中断的区别已经消失,剩下的只有一个:快速中断(使用 SA_INTERRUPT)执行时禁止所有在当前处理器上的其他中断。注意:其他的处理器仍然能够处理中断。
这 个描述是从 2.6 内核 arch/i386/kernel/irq.c, arch/i386/kernel/ apic.c,arch/i386/kernel/entry.S, arch/i386/kernel/i8259.c, 和include/asm-i386/hw_irq.h 中得出,尽管基本概念相同,硬件细节与其他平台上不同。
底 层中断处理代码在汇编语言文件 entry.S。在所有情况下,这个代码将中断号压栈并且跳转到一个公共段,公共段会调用do_IRQ(在 irq.c 中定义)。do_IRQ 做的第一件事是应答中断以便中断控制器能够继续其他事情。它接着获取给定 IRQ号的一个自旋锁,阻止其他 CPU 处理这个 IRQ,然后清除几个状态位(包括IRQ_WAITING )然后查找这个 IRQ的处理例程。若没有找到,什么也不做;释放自旋锁,处理任何待处理的软件中断,最后 do_IRQ 返回。从中断中返回的最后一件事可能是一次处理器的重新调度。
/proc 接口
当硬件中断到达处理器时, 内核提供的一个内部计数器会递增,产生的中断报告显示在文件/proc/interrupts中。这一方法可以用来检查设备是否按预期地工作。此文件只显示当前已安装处理例程的中断的计数。若以前request_irq的一个中断,现在已经free_irq了,那么就不会显示在这个文件中,但是它可以显示终端共享的情况。
/proc/stat记录了几个关于系统活动的底层统计信息,包括(但不仅限于)自系统启动以来收到的中断数。
以上两个文件的一个不同是:/proc/interrupts几乎不依赖体系,而/proc/stat的字段数依赖内核下的硬件中断,其定义在中。ARM的定义为:
#define
自动检测 IRQ 号
驱动初始化时最迫切的问题之一是决定设备要使用的IRQ线,驱动需要信息来正确安装处理例程。自动检测中断号对驱动的可用性来说是一个基本需求。有时自动探测依赖一些设备具有的默认特性,以下是典型的并口中断探测程序:
if
有的驱动允许用户在加载时覆盖默认值:
insmod xxxxx.koirq=x
当目标设备有能力告知驱动它要使用的中断号时,自动探测中断号只是意味着探测设备,无需做额外的工作探测中断。
但不是每个设备都对程序员友好,对于他们还是需要一些探测工作。这个工作技术上非常简单:驱动告知设备产生中断并且观察发生了什么。如果一切顺利,则只有一个中断信号线被激活。尽管探测在理论上简单,但实现可能不简单。有 2种方法来进行探测中断:
(1)调用内核定义的辅助函数
unsigned
int
程序员应当注意在调用 probe_irq_on 之后启用设备上的中断, 并在调用 probe_irq_off 前禁用。此外还必须记住在probe_irq_off 之后服务设备中待处理的中断。
以下是LDD3中的并口示例代码,(并口的管脚 9 和 10 连接在一起,探测五次失败后放弃):
int
do
{
}
if
最好只在模块初始化时探测中断线一次。
大部分体系定义了这两个函数( 即便是空的 )来简化设备驱动的移植。
(2)DIY探测
DIY探测与前面原理相同: 使能所有未使用的中断,接着等待并观察发生什么。我们对设备的了解:通常一个设备能够使用3或4个IRQ 号中的一个来进行配置,只探测这些 IRQ号使我们能不必测试所有可能的中断就探测到正确的IRQ 号。
下面的LDD3中的代码通过测试所有"可能的"中断并且察看发生的事情来探测中断。 trials数组列出要尝试的中断, 以 0 作为结尾标志; tried 数组用来跟踪哪个中断号已经被这个驱动注册。
int
int
int
for
do
{
for
if
以下是handler的源码:
irqreturn_t short_probing(int
{
}
若事先不知道"可能的" IRQ
处理例程的参数及返回值
传递给一个中断处理例程的参数有:
int irq(中断号):若要打印 log 消息时,是很有用。
void*dev_id:一 种用户数据类型(驱动程序可用的私有数据),传递给 request_irq的 void*参数,会在中断发生时作为参数传给处理例程。我们通常传递一个指向设备数据结构的指针到 dev_id中,这样一个管理若干相同设备的驱动在中断处理例程中不需要任何额外的代码,就可以找出哪个设备产生了当前的中断事件。
struct pt_regs*regs很少用到。
中断处理例程的典型使用如下:
static
{
}
和这个处理例程关联的打开代码如下:
static
{
}
中断处理例程应当返回一个值指示是否真正处理了一个中断。如果处理例程发现设备确实需要处理, 应当返回 IRQ_HANDLED;否则返回值 IRQ_NONE。以下宏可产生返回值:
IRQ_RETVAL(handled)
有位网友在处理返回值是按惯例return
typedef
#define
#define
#define
实现中断处理例程
中断处理例程唯一的特别之处在中断时运行,它能做的事情受到了一些限制. 这些限制与我们在内核定时器上看到的相同:
(1)中断处理例程不能与用户空间传递数据, 因为它不在进程上下文执行;
(2)中断处理例程也不能做任何可能休眠的事情, 例如调用 wait_event, 使用除 GFP_ATOMIC之外任何东西来分配内存, 或者锁住一个信号量;
(3)处理者不能调用schedule()。
中断处理例程的作用是:将关于中断接收的信息反馈给设备并根据被服务的中断的含义读、写数据。中断处理例程第一步常常包括清除设备的一个中断标志位,大部分硬件设备在清除"中断挂起"位前不会再产生中断。这也要根据硬件的工作原理决定,这一步也可能需要在最后做而不是开始; 这里没有通用的规则。一些设备不需要这步, 因为它们没有一个"中断挂起"位;这样的设备是少数。
一个中断处理的典型任务是:如果中断通知它所等待的事件已经发生(例如新数据到达),就会唤醒休眠在设备上的进程。
不管是快速或慢速处理例程,程序员应编写执行时间尽可能短的处理例程。 如果需要进行长时间计算, 最好的方法是使用 tasklet 或者workqueue 在一个更安全的时间来调度计算任务。
启用和禁止中断
有时设备驱动必须在一段时间(希望较短)内阻塞中断发生。并必须在持有一个自旋锁时阻塞中断,以避免死锁系统。注意:应尽量少禁止中断,即使是在设备驱动中,且这个技术不应当用于驱动中的互斥机制。
有时(但是很少!)一个驱动需要禁止一个特定中断。但不推荐这样做,特别是不能禁止共享中断(在现代系统中,共享的中断是很常见的)。内核提供了 3 个函数,是内核 API 的一部分,声明在 :
void
void
void
在 2.6 内核, 可使用下面 2 个函数中的任一个(定义在 )关闭当前处理器上所有中断:
void
void
void
void
顶半部和底半部
中断处理需要很快完成并且不使中断阻塞太长,所以中断处理的一个主要问题是如何在处理例程中完成耗时的任务。
Linux (连同许多其他系统)通过将中断处理分为两部分来解决这个问题:
“顶半部”:是实际响应中断的例程(request_irq
“底半部”:是被顶半部调度,并在稍后更安全的时间内执行的函数。
他们最大的不同在底半部处理例程执行时,所有中断都是打开的(这就是所谓的在更安全的时间内运行)。典型的情况是:顶半部保存设备数据到一个设备特定的缓存并调度它的底半部,最后退出:
Linux
(1)
(2)工作队列,
中断共享
Linux
安装共享的处理例程
通过
(1)当request_irq
(2)dev_id
内核为每个中断维护一个中断共享处理例程列表,dev_id
请求一个共享的中断时,如果满足下列条件之一,则request_irq
(1)中断线空闲;
(2)所有已经注册该中断信号线的处理例程也标识了IRQ是共享。
一个共享的处理例程必须能够识别自己的中断,并且在自己的设备没有被中断时快速退出(返回
共享处理例程没有探测函数可用,但使用的中断信号线是空闲时标准的探测机制才有效。
一个使用共享处理例程的驱动需要小心:不能使用
中断驱动的
当与驱动程序管理的硬件间的数据传送可能因为某种原因而延迟,驱动编写者应当实现缓存。一个好的缓存机制需采用中断驱动的
为正确进行中断驱动的数据传送,硬件应能够按照下列语义产生中断:
输入:当新数据到达时并处理器准备好接受时,设备中断处理器。
输出:当设备准备好接受新数据或确认一个成功的数据传送时,设备产生中断。
- ldd3学习之七:中断处理
- ldd3学习之七:中断处理
- LDD3学习笔记(13):中断处理
- LDD3学习-第十章-中断处理<一>
- LDD3学习-第十章-中断处理<二>
- LDD3读书笔记----中断处理
- LDD3源码分析之与硬件通信&中断处理
- LDD3源码分析之与硬件通信&中断处理
- LDD3源码分析之与硬件通信&中断处理
- LDD3源码分析之与硬件通信&中断处理
- LDD3源码分析之与硬件通信&中断处理
- [LDD3阅读笔记]中断处理
- [LDD3速记]_中断处理
- LDD3源码学习日记<七>
- ldd3读书笔记:x86的中断处理顺序
- LDD3学习之short
- (七)c52学习之旅-中断
- ldd3之模块学习总结
- Device drivers on SMP systems
- Device drivers on SMP systems
- linux中断嵌套以及中断丢失
- linux中断嵌套以及中断丢失
- ldd3学习之七:中断处理
- ldd3学习之七:中断处理
- ARM+Linux中断系统详细分析
- ARM+Linux中断系统详细分析
- 重入和中断处理程序
- 重入和中断处理程序
- Linux 分区指南《Ubuntu Server 最佳方案》
- ARM中断原理, 中断嵌套的误区…
- ARM中断原理, 中断嵌套的误区…
- linux 中断嵌套整理