首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

linux驱动程序开发中断处理

linux驱动程序开发中断处理

linux驱动程序开发中断处理

Linux 处理中断非常类似它处理用户空间信号的方式. 对大部
分来说, 一个驱动只需要为它的设备中断注册一个处理函数, 并且当它们到来
时正确处理它们. 当然, 在这个简单图像之下有一些复杂; 特别地, 中断处理
有些受限于它们能够进行的动作, 这是它们如何运行而导致的结果.
没有一个真实的硬件设备来产生中断, 就难演示中断的使用. 因此, 本章使用
的例子代码使用并口工作. 这些端口在现代硬件上开始变得稀少, 但是, 运气
地, 大部分人仍然能够有一个有可用的端口的系统. 我们将使用来自上一章的
简短模块; 添加一小部分它能够产生并处理来自并口的中断. 模块的名子,
short, 实际上意味着 short int ( 它是 C, 对不?), 来提醒我们它处理中断.
但是, 在我们进入主题之前, 是时候提出一个注意事项. 中断处理, 由于它们
的特性, 与其他的代码并行地运行. 因此, 它们不可避免地引起并发问题和对
数据结构和硬件的竞争.
10.1. 准备并口
尽管并口简单, 它能够触发中断. 这个能力被打印机用来通知 lp 驱动它准备
好接收缓存中的下一个字符. 如同大部分设备, 并口实际上不产生中断, 在它被指示这样作之前; 并口标准
规定设置 port 2 (0x37a, 0x27a, 或者任何)的 bit 4 就使能中断报告. short
在模块初始化时进行一个简单的 outb 调用来设置这个位.
一旦中断使能, 任何时候在管脚 10 (所谓的 ACK 位)上的电信号从低变到高,
并口产生一个中断. 最简单的方法来强制接口产生中断( 没有挂一个打印机到
端口 )是连接并口连接器的管脚 9 和 管脚 10. 一根短线, 插到你的系统后面
的并口连接器的合适的孔中, 就建立这个连接. 并口外面的管脚图示于图并口
的管脚
管脚 9 是并口数据字节的最高位. 如果你写二进制数据到 /dev/short0, 你产
生几个中断. 然而, 写 ASCII 文本到这个端口不会产生任何中断, 因为 ASCII
字符集没有最高位置位的项.
如果你宁愿避免连接管脚到一起, 而你手上确实有一台打印机, 你可用使用一
个真正的打印机来运行例子中断处理, 如同下面展示的. 但是, 注意我们介绍
的探测函数依赖管脚 9 和管脚 10 之间的跳线在位置上, 并且你需要它使用你
的代码来试验探测.
10.2. 安装中断处理

中断线是一个宝贵且常常有限的资源, 特别当它们只有 15 或者 16 个时. 内
核保持了中断线的一个注册, 类似于 I/O 端口的注册. 一个模块被希望来请求
一个中断通道(或者 IRQ, 对于中断请求), 在使用它之前, 并且当结束时释放
它. 在很多情况下, 也希望模块能够与其他驱动共享中断线, 如同我们将看到
的. 下面的函数, 声明在 <linux/interrupt.h>, 实现中断注册接口:
int request_irq(unsigned int irq,
                irqreturn_t (*handler)(int, void *, struct pt_regs *),
                unsigned long flags,

                const char *dev_name,
                void *dev_id);

void free_irq(unsigned int irq, void *dev_id);
从 request_irq 返回给请求函数的返回值或者是 0 指示成功, 或者是一个负
的错误码, 如同平常. 函数返回 -EBUSY 来指示另一个驱动已经使用请求的中
断线是不寻常的. 函数的参数如下:
unsigned int irq  请求的中断号
irqreturn_t (*handler)  
安装的处理函数指针. 我们在本章后面讨论给这个函数的参数以及它的
返回值.
unsigned long flags  
如你会希望的, 一个与中断管理相关的选项的位掩码(后面描述).
const char *dev_name  
这个传递给 request_irq 的字串用在 /proc/interrupts 来显示中断的
拥有者(下一节看到)
void *dev_id  
用作共享中断线的指针. 它是一个独特的标识, 用在当释放中断线时以
及可能还被驱动用来指向它自己的私有数据区(来标识哪个设备在中断).
如果中断没有被共享, dev_id 可以设置为 NULL, 但是使用这个项指向
设备结构不管如何是个好主意. 我们将在"实现一个处理"一节中看到
dev_id 的一个实际应用.
flags 中可以设置的位如下:
SA_INTERRUPT  
当置位了, 这表示一个"快速"中断处理. 快速处理在当前处理器上禁止
中断来执行(这个主题在"快速和慢速处理"一节涉及).
SA_SHIRQ  
这个位表示中断可以在设备间共享. 共享的概念在"中断共享"一节中略
述.
SA_SAMPLE_RANDOM  
这个位表示产生的中断能够有贡献给 /dev/random 和 /dev/urandom 使
用的加密池. 这些设备在读取时返回真正的随机数并且设计来帮助应用
程序软件为加密选择安全钥. 这样的随机数从一个由各种随机事件贡献
的加密池中提取的. 如果你的设备以真正随机的时间产生中断, 你应当
设置这个标志. 如果, 另一方面, 你的中断是可预测的( 例如, 一个帧
抓取器的场消隐), 这个标志不值得设置 -- 它无论如何不会对系统加密
有贡献. 可能被攻击者影响的设备不应当设置这个标志; 例如, 网络驱动易遭受从外部计时的可预测报文并且不应当对加密池有贡献. 更多信
息看 drivers/char/random.c 的注释.  
中断处理可以在驱动初始化时安装或者在设备第一次打开时. 尽管从模块的初
始化函数中安装中断处理可能听来是个好主意, 它常常不是, 特别当你的设备
不共享中断. 因为中断线数目是有限的, 你不想浪费它们. 你可以轻易使你的
系统中设备数多于中断数.如果一个模块在初始化时请求一个 IRQ, 它阻止了任
何其他的驱动使用这个中断, 甚至这个持有它的设备从不被使用. 在设备打开
时请求中断, 另一方面, 允许某些共享资源.  
例如, 可能与一个 modem 在同一个中断上运行一个帧抓取器, 只要你不同时使
用这 2 个设备. 对用户来说是很普通的在系统启动时为一个特殊设备加载模块,
甚至这个设备很少用到. 一个数据获取技巧可能使用同一个中断作为第 2 个串
口. 虽然不是太难避免在数据获取时联入你的互联网服务提供商(ISP), 被迫卸
载一个模块为了使用 modem 确实令人不快.
调用 request_irq 的正确位置是当设备第一次打开时, 在硬件被指示来产生中
断前. 调用 free_irq 的位置是设备最后一次被关闭时, 在硬件被告知不要再
中断处理器之后. 这个技术的缺点是你需要保持一个每设备的打开计数, 以便
于你知道什么时候中断可以被禁止.
尽管这个讨论, short 还在加载时请求它的中断线. 这样做是为了你可以运行
测试程序而不必运行一个额外的进程来保持设备打开. short, 因此, 从它的初
始化函数( short_init )请求中断, 不是在 short_open 中做, 象一个真实设备
驱动.
下面代码请求的中断是 short_irq. 变量的真正赋值(即, 决定使用哪个 IRQ )
在后面显示, 因为它和现在的讨论无关. short_base 是使用的并口 I/O 基地
址; 接口的寄存器 2 被写入来使能中断报告.
if (short_irq >= 0)
{
        result = request_irq(short_irq, short_interrupt,
                             SA_INTERRUPT, "short", NULL);
        if (result) {
                printk(KERN_INFO "short: can't get assigned irq %i\n",
                       short_irq);

                short_irq = -1;
        } else { /* actually enable it -- assume this *is* a parallel port
*/
                outb(0x10,short_base+2);
        }
} 代码显示, 安装的处理是一个快速处理(SA_INTERRUPT), 不支持中断共享
(SA_SHIRQ 没有), 并且不对系统加密有贡献(SA_SAMPLE_RANDOM 也没有).
outb 调用接着为并口使能中断报告.
由于某些合理原因, i386 和 x86_64 体系定义了一个函数来询问一个中断线的
能力:
int can_request_irq(unsigned int irq, unsigned long flags);  
这个函数当试图分配一个给定中断成功时返回一个非零值. 但是, 注意, 在
can_request_irq 和 request_irq 的调用之间事情可能一直改变.
10.2.1. /proc 接口
无论何时一个硬件中断到达处理器, 一个内部的计数器递增, 提供了一个方法
来检查设备是否如希望地工作. 报告的中断显示在 /proc/interrupts. 下面的
快照取自一个双处理器 Pentium 系统:
[email=root@montalcino:/bike/corbet/write/ldd3/src/short]root@montalcino:/bike/corbet/write/ldd3/src/short[/email]# m /proc/interrupts
        CPU0     CPU1  
0:  4848108       34   IO-APIC-edge  timer  
2:        0        0         XT-PIC  cascade  
8:        3        1   IO-APIC-edge  rtc  
10:    4335        1  IO-APIC-level  aic7xxx  
11:    8903        0  IO-APIC-level  uhci_hcd  
12:      49        1   IO-APIC-edge  i8042   
NMI:       0        0   
LOC: 4848187  4848186   
ERR:       0   
MIS:       0   
第一列是 IRQ 号. 你能够从没有的 IRQ 中看到这个文件只显示对应已安装处
理的中断. 例如, 第一个串口(使用中断号 4)没有显示, 指示 modem 没在使用.
事实上, 即便如果 modem 已更早使用了, 但是在这个快照时间没有使用, 它不
会显示在这个文件中; 串口表现很好并且在设备关闭时释放它们的中断处理.
/proc/interrupts 的显示展示了有多少中断硬件递交给系统中的每个 CPU. 如
同你可从输出看到的, Linux 内核常常在第一个 CPU 上处理中断, 作为一个使
cache 局部性最大化的方法.[37]
最后 2 列给出关于处理中断的可编程中断控制
器的信息(驱动编写者不必关心), 以及已注册的中断处理的设备的名子(如同在
给 request_irq 的参数 dev_name 中指定的).
/proc 树包含另一个中断有关的文件, /proc/stat; 有时你会发现一个文件更
加有用并且有时你会喜欢另一个. /proc/stat 记录了几个关于系统活动的低级统计量, 包括(但是不限于)自系统启动以来收到的中断数. stat 的每一行以一
个文本字串开始, 是该行的关键词; intr 标志是我们在找的. 下列(截短了)快
照是在前一个后马上取得的:
intr 5167833 5154006 2 0 2 4907 0 2 68 4 0 4406 9291 50 0 0  
第一个数是所有中断的总数, 而其他每一个代表一个单个 IRQ 线, 从中断 0
开始. 所有的计数跨系统中所有处理器而汇总的. 这个快照显示, 中断号 4 已
使用 4907 次, 尽管当前没有安装处理. 如果你在测试的驱动请求并释放中断
在每个打开和关闭循环, 你可能发现 /proc/stat 比 /proc/interrupts 更加
有用.
2 个文件的另一个不同是, 中断不是体系依赖的(也许, 除了末尾几行), 而
stat 是; 字段数依赖内核之下的硬件. 可用的中断数目少到在 SPARC 上的 15
个, 多到 IA-64 上的 256 个, 并且其他几个系统都不同. 有趣的是要注意,
定义在 x86 中的中断数当前是 224, 不是你可能期望的 16; 如同在
include/asm-i386/irq.h 中解释的, 这依赖 Linux 使用体系的限制, 而不是
一个特定实现的限制( 例如老式 PC 中断控制器的 16 个中断源).
下面是一个 /proc/interrupts 的快照, 取自一台 IA-64 系统. 如你所见, 除
了不同硬件的通用中断源的路由, 输出非常类似于前面展示的 32-位 系统的输
出.
         CPU0     CPU1  
27:     1705    34141  IO-SAPIC-level  qla1280  
40:        0        0  SAPIC         
  perfmon  
43:      913     6960  IO-SAPIC-level  eth0  
47:    26722      146  IO-SAPIC-level  usb-uhci  
64:        3        6  IO-SAPIC-edge   ide0  
80:        4        2  IO-SAPIC-edge   keyboard  
89:        0        0  IO-SAPIC-edge   PS/2 Mouse   
239:  5606341  5606052          SAPIC   timer   

254:  67575  52815  SAPIC  IPI   
NMI:  0  0   
ERR:  0   
文章来源:http://cyuyanbiancheng.blog.hexun.com/p2/default.aspx
返回列表