韦东山 IMX6ULL和正点原子_【预习】韦东山:剥丝抽茧分析Linux中断系统中的重要数据结构...

导语:

众所周知,目前升级版视频正在录中断系统,已经录到【Linux系统对中断处理的演进】,配套文档发布后,颇受学员好评,知乎文章:

https://zhuanlan.zhihu.com/p/113002536

获赞147,成为爆文,是linux专题的热门文章,一跃成为专栏文章点赞TOP4:

e18c4f877ed6c3247cad6e9d5c75bf65.png

由此可见文章受欢迎的程度以及需求的强烈。韦东山老师再接再厉,不辜负学员的期望,正在录【Linux中断系统中的重要数据结构】,本文是该视频配套文档(视频正在录制),拿出来让大家尝鲜,以飨读者。


作者:韦东山

正文:

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

ebf3a6aa4ff8e4983345b3f513076d98.png

编辑已经尽力,需高清图可以查看配套文档或微信联系13163769879

最核心的结构体是irq_desc,之前为了易于理解,我们说在Linux内核中有一个中断数组,对于每一个硬件中断,都有一个数组项,这个数组就是irq_desc数组。

注意

如果内核配置了CONFIG_SPARSE_IRQ,那么它就会用基数树(radix tree)来代替irq_desc数组。SPARSE的意思是“稀疏”,假设大小为1000的数组中只用到2个数组项,那不是浪费嘛?所以在中断比较“稀疏”的情况下可以用基数树来代替数组。

1.irq_desc数组

irq_desc结构体在include/linux/irqdesc.h中定义,主要内容如下图:

5061aef5c1dbbf85c6bd78bb1636f85b.png

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

2014d2868d82d4b46ce3026f1b48784c.png

外部设备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数组如下:

06f83d1a34d8bb1731fcc3b06b4ebb16.png

2.irqaction结构体

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

ed94c9cf01f7e3ab4713a2858ae5a61e.png

当调用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中定义,主要内容如下图:

72ea7ec0206335b36e54ffa579a187cd.png

它就是个中转站,里面有irq_chip指针 irq_domain指针,都是指向别的结构体。

比较有意思的是irq、hwirq,irq是软件中断号,hwirq是硬件中断号。比如上面我们举的例子,在GPIO中断B是软件中断号,可以找到irq_desc[B]这个数组项;GPIO里的第x号中断,这就是hwirq。

谁来建立irq、hwirq之间的联系呢?由irq_domain来建立。irq_domain会把本地的hwirq映射为全局的irq,什么意思?比如GPIO控制器里有第1号中断,UART模块里也有第1号中断,这两个“第1号中断”是不一样的,它们属于不同的“域”──irq_domain。

4.irq_domain结构体

irq_domain结构体在include/linux/irqdomain.h中定义,主要内容如下图:

b18c29173f50eff85fc4eaede62304c0.png

当我们后面从设备树讲起,如何在设备树中指定中断,设备树的中断如何被转换为irq时,irq_domain将会起到极大的作为。

这里基于入门的解度简单讲讲,在设备树中你会看到这样的属性:

interrupt-parent = ;interrupts = <5 IRQ_TYPE_EDGE_RISING>;

它表示要使用gpio1里的第5号中断,hwirq就是5。

但是我们在驱动中会使用request_irq(irq, handler)这样的函数来注册中断,irq是什么?它是软件中断号,它应该从“gpio1的第5号中断”转换得来。

谁把hwirq转换为irq?由gpio1的相关数据结构,就是gpio1对应的irq_domain结构体。

irq_domain结构体中有一个irq_domain_ops结构体,里面有各种操作函数,主要是:

① xlate

用来解析设备树的中断属性,提取出hwirq、type等信息。

② map

把hwirq转换为irq。

5.irq_chip结构体

irq_chip结构体在include/linux/irq.h中定义,主要内容如下图:

310a3f904ee3c9e41ff3830d7cf1b809.png

这个结构体跟“chip”即芯片相关,里面各成员的作用在头文件中也列得很清楚,摘录部分如下:

* @irq_startup:  start up the interrupt (defaults to ->enable if NULL)* @irq_shutdown:  shut down the interrupt (defaults to ->disable if NULL)* @irq_enable:    enable the interrupt (defaults to chip->unmask if NULL)* @irq_disable:  disable the interrupt* @irq_ack:    start of a new interrupt* @irq_mask:    mask an interrupt source* @irq_mask_ack:  ack and mask an interrupt source* @irq_unmask:    unmask an interrupt source* @irq_eoi:    end of interrupt

我们在request_irq后,并不需要手工去使能中断,原因就是系统调用对应的irq_chip里的函数帮我们使能了中断。

我们提供的中断处理函数中,也不需要执行主芯片相关的清中断操作,也是系统帮我们调用irq_chip中的相关函数。

但是对于外部设备相关的清中断操作,还是需要我们自己做的。

就像上面图里的“外部设备1“、“外部设备n”,外设备千变万化,内核里可没有对应的清除中断操作。

ps:如果您看完有一种只知道结论,也想了解原因或分析过程请看视频。

☆ END ☆

我是韦东山,10多年一直在研究linux+ARM,希望我的分享对你有帮助,欢迎进店订阅我的付费内容:100ask.taobao.com

往期好文推荐:

【深度】韦东山:一文看尽 linux对中断处理的前世今生

【调试笔记】韦东山:在100ASK_IMX6ULL板子上支持其他型号的屏幕

【长文】说说UBOOT的几个核心问题

联系我们:

如果您没有没懂没关系,可以加韦东山官方微信群进行讨论。

先加管理员微信:13163769879,若购买产品,拉您到对应的售后群,否则拉入粉丝群。

最后,如果本文对您有帮助,请给我们一个转发或在看,您的支持是我们更新的不断动力。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值