【原创】《Linux设备驱动程序》学习之循序渐进 --- 中断处理

来源:互联网 发布:淘宝会员抓取是真的吗 编辑:程序博客网 时间:2024/06/02 12:35


【原创】《Linux设备驱动程序》学习之循序渐进 --- 中断处理


第十章 --- 中断处理

安装中断处理例程

中断处理可以在驱动初始化时或者在设备第一次打开时安装.

调用 request_irq 的正确位置是当设备第一次打开时, 在硬件被指示来产生中断前. 调用 free_irq 的位置是设备最后一次被关闭时, 在硬件被告知不要再中断处理器之后. 这个技术的缺点是你需要保持一个每设备的打开计数, 以便于你知道什么时候中断可以被禁止. 

实现中断处理例程

一个处理者不能传递数据到或者从
用户空间, 因为它不在进程上下文执行. 处理者也不能做任何可能睡眠的事情, 例如调用 wait_event, 使用除 GFP_ATOMIC 之外任何东西来分配内存, 或者加锁一个信号量. 最后, 处理者不能调用调度. 

程序员应当小心编写一个函数在最小量的时间内执行, 不管是一个快速或慢速处理者. 如果需要进行长时间计算, 最好的方法是使用一个 tasklet 或者 workqueue 来调度计算在一个更安全的时间(我们将在"上和下半部"一节中见到工作如何被延迟.). 

上半部和下半部

Linux (许多其他系统一起)解决这个问题通过将中断处理分为 2 半. 所谓的前半部是实际响应中断的函数 -- 你使用 request_irq 注册的那个. 后半部是由前半部调度来延后执行的函数, 在一个更安全的时间. 最大的不同在前半部处理和后半部之间是所有的中断在后半部执行时都使能 -- 这就是为什么它在一个更安全时间运行. 在典型的场景中, 前半部保存设备数据到一个设备特定的缓存, 调度它的后半部, 并且退出: 这个操作非常快. 后半部接着进行任何其他需要的工作, 例如唤醒进程, 启动另一个 I/O 操作, 等等. 这种设置允许前半部来服务一个新中断而同时后半部仍然在工作.

Linux 内核有 2 个不同的机制可用来实现后半部处理, 我们都在第 7 章介绍. tasklet 常常是后半部处理的首选机制; 它们非常快, 但是所有的 tasklet 代码必须是原子的. tasklet 的可选项是工作队列, 它可能有一个更高的运行周期但是允许睡眠.

tasklet

记住 tasklet 是一个特殊的函数, 可能被调度来运行, 在软中断上下文, 在一个系统决定的安全时间中. 它们可能被调度运行多次, 但是 tasklet 调度不累积; ;tasklet 只运行一次, 即便它在被投放前被重复请求.

另一个中断当然可能在 tasklet 在运行时被递交, 因此, tasklet 和中断处理之间加锁可能仍然需要.

中断共享

如果你需要探测你的设备, 在请求 IRQ 线之前, 内核无法帮你. 没有探测函数可给共享处理者使用.

一个功能齐全的驱动可能将工作划分位前和后半部, 但是容易添加并且不会有任何影响实现共享的代码. 一个真实驱动还可能使用 dev_id 参数来决定, 在很多可能的中, 哪个设备在中断. 

快速参考 

#include <linux/interrupt.h> 
int  request_irq(unsigned  int  irq,  irqreturn_t  (*handler)(  ),  unsigned  long  flags,  const  char 
*dev_name, void *dev_id); 
void free_irq(unsigned int irq, void *dev_id); 
调用这个注册和注销一个中断处理. 
#include <linux/irq.h.h> 
int can_request_irq(unsigned int irq, unsigned long flags); 
这个函数, 在 i386 和 x86_64 体系上有, 返回一个非零值如果一个分配给定中断线的企图成功. 
#include <asm/signal.h> 

SA_INTERRUPT 
SA_SHIRQ 
SA_SAMPLE_RANDOM  
给 request_irq 的标志. SA_INTERRUPT 请求安装一个快速处理者( 相反是一个慢速的). SA_SHIRQ 安装一个共享的处理者, 并且第 3 个 flag 声称中断时戳可用来产生系统熵. 
/proc/interrupts 
/proc/stat  
报告硬件中断和安装的处理者的文件系统节点. 
unsigned long probe_irq_on(void); 
int probe_irq_off(unsigned long); 
驱动使用的函数, 当它不得不探测来决定哪个中断线被设备在使用. probe_irq_on 的结果必须传回给 probe_irq_off 在中断产生之后. 
probe_irq_off 的返回值是被探测的中断号. 
IRQ_NONE 
IRQ_HANDLED 
IRQ_RETVAL(int x) 
从一个中断处理返回的可能值, 指示是否一个来自设备的真正的中断出现了. 
void disable_irq(int irq); 
void disable_irq_nosync(int irq); 
void enable_irq(int irq); 
驱动可以使能和禁止中断报告. 如果硬件试图在中断禁止时产生一个中断, 这个中断永远丢失了. 一个使用一个共享处理者的驱动必须不使用这个函数. 
void local_irq_save(unsigned long flags); 
void local_irq_restore(unsigned long flags); 
使用 local_irq_save 来禁止本地处理器的中断并且记住它们之前的状态. flags 可以被传递给 local_irq_restore 来恢复之前的中断状态. 
void local_irq_disable(void); 
void local_irq_enable(void); 
在当前处理器熵无条件禁止和使能中断的函数. 

原文链接:

http://blog.csdn.net/geng823/article/details/38084321

0 0
原创粉丝点击