大家好,又见面了,我是全栈君。
- 概述:系统要及时的感知硬件的状态,通常有两种方式:一种是轮询。一种是通过响应硬件中断。前者会浪费处理器的时间,而后者不会。
- 准备并口
- 在没有节设定产生中断之前,并口是不会产生中断的
- 并口的标准规定设置port2(px37a、0x27a或者其他port)的第4位将启用中断报告。0×10
- 当处于启用中断状态。每当引脚10的电平发生从低到高改变时。并口就会产生一个中断
- 引脚9是并口数据字节中的最高位
- 安装中断处理例程
- 中断信号线是很珍贵且有限的资源
- 内核维护了一个中断信号线的注冊表。该注冊表类似于I/Oport的注冊表
- 模块在使用中断前要先请求一个中断通道,然后在使用后释放该通道
- <linux/sched.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);
- 返回0表示申请成功
- flags
- SA_INTERRUPT
- 表明这是一个“高速”的中断处理例程
- SA_SHIRQ
- 表示中断能够在设备之间共享
- SA_SAMPLE_RANDOM
- 指出产生的中断能对/dev/random设备和/dev/urandom设备使用的熵池(entropy pool)有贡献
- void free_irq(unsigned int irq, void *dev_id);
- int can_request_irq(unsigned int irq, unsigned long flags);
- int request_irq(unsigned int irq, irqreturn_t (*handler) (int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id);
- 使用request_irq的正确位置应该是在设备第一次打开、硬件被告知产生中断之前
- 调用free_irq的位置是最后一次关闭设备、硬件被告知不用再中断处理器之后
- /proc接口
- /proc/interrupts
- 不依赖体系结构
- /proc/stat
- 依赖体系结构
- 当前x86体系结构上定义的中断数量是224,能够从头文件include/asm-386/irq.h中得到解释
- /proc/interrupts
- 自己主动检測IRQ号
- 内核帮助下的探測
- <linux/interrupt.h>
- unsigned long probe_irq_on(void);
- int probe_irq_off(unsigned long);
- <linux/interrupt.h>
- DIY探測
- 内核帮助下的探測
- 高速和慢速处理例程
- 高速中断运行时。当前处理器上的其它全部中断都被禁止
- x86平台上中断处理的内幕
- 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
- IRQ的探測是通过为每一个缺少中断处理例程的IRQ设置IRQ_WAITING状态位来完毕的
- 实现中断处理例程
- 中断处理例程是在中断时间内执行的,因此它的行为会受到一些限制
- 不能向用户空间发送或者接收数据
- 不能做作不论什么可能发生休眠的操作
- 不能调用schdule函数
- 将有关中断接收到信息反馈给设备,并依据正在服务的中断的不同含义对数据进行对应的读或写
- 通常做清除接口卡上的一个位。大多数硬件设备在它们的“interrupt-pending(中断挂起)”位被清除之前不会产生其它的中断
- 中断处理例程的一个典型任务就是:假设中断通知进程所等待的事件已经发生。比方新的数据到达。就会唤醒在该设备上休眠的进程
- 处理例程的參数及返回值
- int irq是中断号
- void *dev_id是一种客户数据类型(即驱动程序可用的私有数据)
- struct pt_reg *regs非常少使用。它保存了处理器进入中断代码之前的处理器上下文快照
- 中断处理例程应该返回一个值。用来指明是否真正处理了一个中断。假设处理例程发现其设备的确须要处理,则应该返回IRQ_HANDLED。否则。返回值应该是IRQ_NONE
- 启用和禁用中断
- 有时设备驱动程序必须在一个时间段内堵塞中断的发出,如拥有自旋锁的时候堵塞中断
- 禁用单个中断
- <asm/irq.h>
- void disable_irq(int irq);
- void disable_irq_nosync(int irq);
- void enable_irq(int irq);
- diables_irq不但会禁止给定的中断,并且也会等待当前正在运行的中断处理例程完毕
- 假设调用disable_irq的线程拥有不论什么中断处理例程须要的资源(比方自旋锁),则系统会死锁
- <asm/irq.h>
- 禁用全部的中断
- <asm/system.h>
- void local_irq_save(unsigned long falsg);
- void local_irq_disable(void);
- void local_irq_restore(unsigned long flags);
- void local_irq_enable(void)
- <asm/system.h>
- 中断处理例程是在中断时间内执行的,因此它的行为会受到一些限制
- 顶半部和底半部
- 顶半部。是实际响应中断的例程。也就是用request_irq注冊的中断例程
- 底半部,是一个被顶半部调度。并在稍后更安全的时间内运行的例程
- 当底半部处理例程运行时,全部的中断都是打开的
- 典型的情况是顶半部保存设备的数据到一个设备特定的缓冲区并调度它的底半部
- tasklet
- tasklet能够被多次调度执行,但tasklet的调度并不会累积
- 假设驱动程序有多个tasklet。必须使用某种锁机制来避免彼此间的冲突
- tasklet可确保和第一次调度它们的函数执行在相同的CPU上
- 必须使用宏DECLARE_TASKLET声明tasklet
- DECLARE_TASKLET(name, function, data);
- void do_tasklet(unsigned long);
- DECLARE_TASKLET(test_tasklet, do_tasklet, 0);
- tasklet_schedule($test_tasklet);
- 工作队列
- 工作队列函数执行在进程上下文中,因此能够必要时休眠
- 不能从工作队列向用户空间复制数据
- 中断共享
- PC上的IRQ信号灯线不能为一个以上的设备服务
- 现代硬件已经能谲诈中断的共享了。PCI总线就要求外设可共享中断
- 安装共享的处理例程
- 共享的中断也是通过request_irq安装的。可是有两处不同
- 请求中断时,必须指定flags參数中的SA_SHIRQ位
- dev_id參数必须是唯一的,不论什么指向模块地址空间的指针都能够使用,但dev_id不能设置成NULL
- 请求一个共享中断时,假设满足以下条件之中的一个。那么request_irq就会成功
- 中断信号线空暇
- 不论什么已经注冊了该中断信号线的处理例程也标识了IRQ是共享的
- 使用共享处理例程的驱动程序须要小心一件事情:不能使用enable_irq和disable_irq
- 共享的中断也是通过request_irq安装的。可是有两处不同
- 执行处理例程
- 当内核收到中断时,全部已注冊的处理例程都将被调用
- 一个共享中断处理例程必须可以将要处理的中断和其它设备产生的中断区分开来
- /proc接口和共享的中断
- 在系统上安装共享的中断处理例程不会对/proc/stat造成影响。它甚至不知道哪些处理例程是共享的,可是,/proc/interrupts会稍许改变
- 中断驱动的I/O
- 假设与驱动程序管理的硬件之间的传输数据由于某种原因被延迟的话,驱动程序作者就应该实现缓冲
- 数据缓冲区有助于将数据的传送和接收与系统调用write和read分离开来,从而提高系统的总体性能
- 一个好的缓冲机制须要用中断驱动的I/O
- 要正确进行中断驱动的传输数据,要求硬件 应该能依照以下的语义来产生中断
- 对于输入来说。当新的数据已经到达而且处理器准备接收它时,设备就中断处理器
- 对于输出来说,当设备准备好接收新数据或者对成功的数据传送进行应答时,就要发出中断
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/115579.html原文链接:https://javaforall.cn