能弄清楚下面这个图,对linux中断系统的掌握也基本到位了 。

文章插图
最核心的结构体是irq_desc,之前为了易于理解,我们说在Linux内核中有一个中断数组,对于每一个硬件中断,都有一个数组项,这个数组就是irq_desc数组 。
注意:
如果内核配置了CONFIG_SPARSE_IRQ,那么它就会用基数树(radix tree)来代替irq_desc数组 。SPARSE的意思是“稀疏”,假设大小为1000的数组中只用到2个数组项,那不是浪费嘛?所以在中断比较“稀疏”的情况下可以用基数树来代替数组 。
1.irq_desc数组irq_desc结构体在include/linux/irqdesc.h中定义,主要内容如下图:

文章插图
每一个irq_desc数组项中都有一个函数:handle_irq,还有一个action链表 。要理解它们,需要先看中断结构图:

文章插图
外部设备1、外部设备n共享一个GPIO中断B,多个GPIO中断汇聚到GIC(通用中断控制器)的A号中断,GIC再去中断CPU 。那么软件处理时就是反过来,先读取GIC获得中断号A,再细分出GPIO中断B,最后判断是哪一个外部芯片发生了中断 。
所以,中断的处理函数来源有三:
① GIC的处理函数:
假设irq_desc[A].handle_irq是XXX_gpio_irq_handler(XXX指厂家),这个函数需要读取芯片的GPIO控制器,细分发生的是哪一个GPIO中断(假设是B),再去调用irq_desc[B]. handle_irq 。
注意:
irq_desc[A].handle_irq细分出中断后B,调用对应的irq_desc[B].handle_irq 。
显然中断A是CPU感受到的顶层的中断,GIC中断CPU时,CPU读取GIC状态得到中断A 。
② 模块的中断处理函数:
比如对于GPIO模块向GIC发出的中断B,它的处理函数是irq_desc[B].handle_irq 。
BSP开发人员会设置对应的处理函数,一般是handle_level_irq或handle_edge_irq,从名字上看是用来处理电平触发的中断、边沿触发的中断 。
注意:
导致GPIO中断B发生的原因很多,可能是外部设备1,可能是外部设备n,可能只是某一个设备,也可能是多个设备 。所以irq_desc[B].handle_irq会调用某个链表里的函数,这些函数由外部设备提供 。这些函数自行判断该中断是否自己产生,若是则处理 。
③ 外部设备提供的处理函数:
这里说的“外部设备”可能是芯片,也可能只是简单的按键 。它们的处理函数由自己驱动程序提供,这是最熟悉这个设备的“人”:它知道如何判断设备是否发生了中断,如何处理中断 。
对于共享中断,比如GPIO中断B,它的中断来源可能有多个,每个中断源对应一个中断处理函数 。所以irq_desc[B]中应该有一个链表,存放着多个中断源的处理函数 。
一旦程序确定发生了GPIO中断B,那么就会从链表里把那些函数取出来,一一执行 。
这个链表就是action链表 。
对于我们举的这个例子来说,irq_desc数组如下:

文章插图
2.irqaction结构体irqaction结构体在include/linux/interrupt.h中定义,主要内容如下图:

文章插图
当调用request_irq、request_threaded_irq注册中断处理函数时,内核就会构造一个irqaction结构体 。在里面保存name、dev_id等,最重要的是handler、thread_fn、thread 。
handler是中断处理的上半部函数,用来处理紧急的事情 。
thread_fn对应一个内核线程thread,当handler执行完毕,Linux内核会唤醒对应的内核线程 。在内核线程里,会调用thread_fn函数 。
可以提供handler而不提供thread_fn,就退化为一般的request_irq函数 。
可以不提供handler只提供thread_fn,完全由内核线程来处理中断 。
也可以既提供handler也提供thread_fn,这就是中断上半部、下半部 。
里面还有一个名为sedondary的irqaction结构体,它的作用以后再分析 。
在reqeust_irq时可以传入dev_id,为何需要dev_id?作用有二:
① 中断处理函数执行时,可以使用dev_id
② 卸载中断时要传入dev_id,这样才能在action链表中根据dev_id找到对应项
所以在共享中断中必须提供dev_id,非共享中断可以不提供 。
3. irq_data结构体irq_data结构体在include/linux/irq.h中定义,主要内容如下图:
推荐阅读
- 为什么少女会长白头发 少女长白发的原因分析
- 调饮玩味普洱茶膏,普洱茶膏DNA分析
- 做自媒体如何写爆文?自媒体爆文分析,自媒体文章写作技巧
- 漏洞分析之thinkPHP反序列化:这就是黑客的世界吗
- 高桥银峰的作用有哪些,高桥银峰营养成分分析
- 渗透测试常用Linux命令总结
- 通过深度系统学习Linux用户和组
- Linux定位c++程序运行异常的经历《实操》
- 详解linux多线程——互斥锁、条件变量、读写锁、自旋锁、信号量
- Linux系统网络相关的常用命令集合
