第七章 中断和中断处理-----读书笔记(Linux kernel development)
一、注册中断处理程序
中断处理程序是管理硬件的驱动程序的组成部分。每个设备都有相关的驱动程序,如果设备使用过中断,那么相应的驱动程序就注册一个中断处理程序。
驱动程序可以通过request_irq()函数注册一个中断处理程序,并且激活给定的中断线。
/* request_irq:分配一条给定的中断线 */
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
第一个参数irq表示要分配的中断号。
第二个参数handler是一个指针,指向处理这个中断的实际中断处理程序。
第三个参数flags可以是0,也可以是下列标志的位掩码:
1、IRQF_DISABLED-------该标志被设置后,意味着内核在处理中断处理程序本身期间,要禁止所有的其他中断。如果不设置,中断处理程序可以与除本身外其他任何中断同时运行。多数中断处理程序是不会设置该位,因为禁止所有中断是一种野蛮行为。
2、IRQF_SAMPLE_RANDOM------此标志表明这个设备产生的中断对内核熵池有贡献。
3、IRQF_TIMER-----该标志是特别为系统定时器的中断处理而准备的。
4、IRQF_SHARED-----该标志表明可以在多个中断处理程序中共享中断线。在同一个给定线上注册的每个处理程序必须指定这个标志;否则,在每条线上只能有一个处理程序。
第四个参数 name 是与中断相关的设备的ASCII文本表示,例如pc机上键盘中断对应的这个值是“keyboard”。
第五个参数dev 用于共享中断线,当一个中断处理程序需要释放时,dev将提供唯一的标志信息,以便从共享中断线的诸多中断处理程序中删除指定的那一个。
request_irq()成功执行会返回0,如果返回非0值,表示有错误发生,这种情况指定的中断处理程序不会被注册。
一个中断例子:在一个驱动程序中请求一个中断线,并在通过request_irq()安装中断处理程序
requset_irq():
if(request_irq(irqn, my_interrupt, IRQF_SHARED, "my_device", my_dev)){
printk(KERN_ERR "my_device: cannot register IRQ %d\n", irqn);
return -EIO;
}
irqn时请求的中断线,my_interrupt是中断处理程序,IRQF_SHARED设置中断线可以共享,设备名“my_device”,将my_dev传递给dev参数。如果返回非0值表示请求失败,打印错误。返回0表示成功。
二、释放中断处理程序
卸载驱动程序时,需要注销相应的中断处理程序,并释放中断线。
void free_irq(unsigned int irq, void *dev)
如果指定的中断线不是共享的,那么删除该中断处理程序的时候会禁用这条中断线,如果中断线共享,就只会删除dev对应的处理程序,而这条中断线本身只有在删除最后一个处理程序时才会禁用。对于共享的中断线,需要一个唯一的信息来区分其上面的多个处理程序,并让free_irq仅仅删除指定的处理程序。唯一的信息就是dev。
三、编写中断处理程序
static irqreturn_t intr_handler(int irq, void *dev)
第一个参数irq就是这个处理程序要响应的中断的中断号。
第二个参数dev是一个通用指针,该值具有唯一确定性,可以用来区分共享同一中断处理程序的多个设备。
中断处理程序的返回值是一个特殊类型:irqreturn_t。中断处理程序可能返回两个特殊的值:IRQ_NONE和IRQ_HANDLED。当中断处理程序检测到一个中断,但该中断对应的设备并不是在注册处理函数期间指定的产生源,返回IRQ_NONE;当中断处理程序被正确调用,且确实是它所对应的设备产生了中断时,返回IRQ_HANDLED。
四、共享的中断处理程序
共享的处理程序与非共享的处理程序的差异主要有以下三处:
1、request_irq()的参数flags必须设置IRQF_SHARED标志
2、对于每个注册的中断程序来说,dev参数必须唯一。不能给共享的处理程序传递NULL值。
3、中断处理程序必须能够区分它的设备是否真的产生中断,这既需要硬件支持,也需要处理程序中相关的处理逻辑。
所有共享中断线的驱动程序都必须满足以上要求,只要有任何一个设备没有按规则进行共享,那么中断线就无法共享了。指定IRQF_SHARED标志以调用request_irq()时,只有在以下两种情况下才可能成功:中断线当前未被注册,或者在该线上的所有已注册处理程序都指定了IRQF_SHARED。
五、中断上下文
当执行一个中断处理程序时,内核处于中断上下文中。中断上下文具有严格的时间限制,因为它打断其他代码。中断上下文中的代码应当迅速,简洁,尽量不要使用循环去处理繁复的工作。
六、中断处理机制的实现
七、中断控制
Linux内核提供了一组接口用于操作机器上的中断状态,这些接口为我们提供了能够禁止当前处理器的中断系统,或屏蔽整个机器的一条中断线的能力。通过禁止中断,可以确保某个中断处理程序不会抢占当前的代码。内核代码一般都需要获取某种锁,防止其他处理对共享数据的并发访问,获取这些锁的同时也伴随着禁止本地中断。锁提供保护机制,防止来自其他处理器对共享数据的并发访问,而禁止中断提供保护机制,则是防止来自其他中断处理程序的并发访问。
八、禁止和激活中断
用于禁止当前处理器上的本地中断,随后又激活它们的语句是:
local_irq_disable();
/* 禁止中断*/
local_irq_enable();
简单的禁止和激活存在潜在危险,在禁止前保存中断系统的状态,在准备激活中断时,恢复状态。
unsigned long flags;
local_irq_save(flags);
/* ...........*/
local_irq_restore(flags);
九、禁止指定中断线
在某些情况下,只禁止整个系统中一条特定的中断线就够了。这就是所谓的屏蔽一条中断线。为此,linux提供了四个接口:
void disable_irq(unsigned int irq);
void disable_irq_nosync(unsigned int irq);
void enable_irq(unsigned int irq);
void synchronize_irq(unsigned int irq);
前两个函数禁止中断控制器上指定的中断线,即禁止给定中断向系统中所有处理器的传递。另外disable_irq() 只有当正在执行的所有处理程序完成后,才返回。函数disable_irq_nosync() 不会等待当前中断处理程序从执行完毕。 函数synchronize_irq()等待一个特定的中断处理程序的退出。 对disable_irq()或disable_irq_nosync()的每次调用,都需要相应调用一次enable_irq()。只有在对enable_irq()完成最后一次调用后,才真正重新激活了中断线。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具