Linux Kernel 中断简析

一、中断基础概念

        所谓中断,指CPU在执行程序的过程中,出现了某些突发事件即待处理,CPU必须暂停当前的程序。转去处理突发事件,处理完毕后CPU又返回原程序被中断的位置并继续执行。

1、中断分类

a -- 内部中断和外部中断

      根据中断的的来源,中断可以分为内部中断和外部中断:

内部中断,其中断源来自CPU内部(软件中断指令、溢出、除法错误等),例如,操作系统从用户态切换到内核态需借助CPU内部的软中断;

外部中断,其中断源来自CPU外部,由外设提出请求;

b -- 可屏蔽中断与不屏蔽中断

      根据中断是否可以屏蔽分为可屏蔽中断与不屏蔽中断:

可屏蔽中断,其可以通过屏蔽字被屏蔽,屏蔽后,该中断不再得到响应;

不屏蔽中断,其不能被屏蔽;

c -- 向量中断和非向量中断

     根据中断入口跳转方法的不同,分为向量中断和非向量中断:

向量中断,采用向量中断的CPU通常为不同的中断分配不同的中断号,当检测到某中断号的中断到来后,就自动跳转到与该中断号对应的地址执行。不同的中断号有不同的入口地址;

非向量中断,其多个中断共享一个入口地址,进入该入口地址后再通过软件判断中断标志来标识具体是哪个中断。

    也就是说,向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务入口地址。

2、中断ID

a -- IRQ number

      cpu给中断的一个编号,一个IRQ number是一个虚拟的interrupt ID,和硬件无关;

b -- HW interrupt ID

       对于中断控制器而言,它收集了多个外设的irq request line,要向cpu传递,GIC要对外设进行编码,GIC就用HW interrupt ID来标示外部中断;

3、SMP情况下中断两种形态

1-Nmode :只有一个processor处理器

N-N :所有的processor都是独立收到中断的

      GIC:SPI使用1-Nmode   PPI 和 sgi使用N-Nmode

二、中断编程

1、 申请IRQ

      在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:

int request_irq(unsigned int irq, irq_handler_t handler,

                         unsigned long irqflags, const char *devname, void *dev_id)

相关参数:

a -- irq是要申请的硬件中断号。另外,这里要思考的问题是,这个irq 是怎么得到的?这里我们在设备树中获取,具体解析见:

b -- handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。

c -- irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)

d -- devname设置中断名称,在cat /proc/interrupts中可以看到此名称。

e -- dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。

      request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。

      顶半部 handler 的类型 irq_handler_t 定义为:

typedef irqreturn_t (*irq_handler_t)(int, void *);

参数1:中断号

参数2 :参数

在Interrupt.h (e:\linux-3.14-fs4412\include\linux)    18323    2014/3/31中定义

IRQ_NONE       共享中断,如果不是我的设备产生的中断,就返回该值

IRQ_HANDLED     中断处理函数正确执行了就返回该值

IRQ_WAKE_THREAD        = (1 << 1)


2、释放IRQ

      与request_irq()相对应的函数为 free_irq(),free_irq()的原型为:

void free_irq(unsigned int irq, void *dev_id)

 free_irq()参数的定义与request_irq()相同。

三、中断注册过程分析

       我们每次用中断的时候就是注册一个中断函数。request_irq首先生成一个irqaction结构,其次根据中断号 找到irq_desc数组项(还记得吧,内核中irq_desc是一个数组,没一项对应一个中断号),然后将irqaction结构添加到 irq_desc中的action链表中。当然还做一些其他的工作,注册完成后,中断函数就可以发生并被处理了。

      irq_desc 内核中记录一个irq_desc的数组,数组的每一项对应一个中断或者一组中断使用同一个中断号,一句话irq_desc几乎记录所有中断相关的东西,这个结构是中断的核心。其中包括俩个重要的结构irq_chip 和irqaction 。

1、  irq_chip

      irq_chip  里面基本上是一些回调函数,其中大多用于操作底层硬件,设置寄存器,其中包括设置GPIO为中断输入就是其中的一个回调函数,分析一些源代码

struct irq_chip {

        const char    *name;

        unsigned int  (*startup)(unsigned int irq); //启动中断

        void          (*shutdown)(unsigned int irq); //关闭中断

        void          (*enable)(unsigned int irq);  // 使能中断

        void          (*disable)(unsigned int irq); // 禁止中断

        void          (*ack)(unsigned int irq);  //中断应答函数,就是清除中断标识函数

        void          (*mask)(unsigned int irq);  //中断屏蔽函数

        void          (*mask_ack)(unsigned int irq); //屏蔽中断应答函数,一般用于电平触发方式,需要先屏蔽再应答

        void          (*unmask)(unsigned int irq);  //开启中断

        void          (*eoi)(unsigned int irq);

        void          (*end)(unsigned int irq);

        int            (*set_affinity)(unsigned int irq,

                                      const struct cpumask *dest);

        int            (*retrigger)(unsigned int irq);

        int            (*set_type)(unsigned int irq, unsigned int flow_type); //设置中断类型,其中包括设置GPIO口为中断输入

        int            (*set_wake)(unsigned int irq, unsigned int on);

        void          (*bus_lock)(unsigned int irq);  //上锁函数

        void          (*bus_sync_unlock)(unsigned int irq); //解锁

        /* Currently used only by UML, might disappear one day.*/

#ifdef CONFIG_IRQ_RELEASE_METHOD

        void          (*release)(unsigned int irq, void *dev_id);

#endif

        /*

        * For compatibility, ->typename is copied into ->name.

        * Will disappear.

        */

        const char    *typename;

};

       我们可以看到这里实现的是一个框架,需要我们进一步的填充里面的函数。我们在分析另一个结构irqaction

2、irqaction

include/linux/interrupt.h

struct irqaction {

        irq_handler_t handler;  //用户注册的中断处理函数

        unsigned long flags;    //中断标识

        const char *name;      //用户注册的中断名字,cat/proc/interrupts时可以看到

        void *dev_id;          //可以是用户传递的参数或者用来区分共享中断

        struct irqaction *next; //irqaction结构链,一个共享中断可以有多个中断处理函数

        int irq;                //中断号

        struct proc_dir_entry *dir;

        irq_handler_t thread_fn;

        struct task_struct *thread;

        unsigned long thread_flags;

};


      我们用irq_request函数注册中断时,主要做俩个事情,根据中断号生成一个irqaction结构并添加到irq_desc中的 action结构链表,另一发面做一些初始化的工作,其中包括设置中断触发方式,设置一些irq_chip结构中没有初始化的函数为默认,开启中断,设置 GPIO口为中断输入模式。


中断发生之后处理流程

a -- 具体的CPU architecture相关模块进行现场保护,然后调用machine driver执行对应的中断处理handler;

b -- machine driver对应中断处理handler会根据硬件的信息获取HW interrupt  id,然后通过irq domain模块翻译成irq number;

c -- 调用该IRQ number对应 hign level irq  event handler,在这个hign level的handler中,会通过和中断控制器交互,进行中断处理的flow 控制(中断的嵌套、抢占),最终会遍历我们注册的IRQ action list,调用对应处理函数;

d -- CPU architeccture相关模块进行现场的恢复;

具体分析之前先看几个基础概念:

一、 中断硬件框架

        中断的主动通知特性需要硬件设施支持。在数字逻辑电路层面,外部设备和处理器之间有一条专门的中断信号线(Interrupt Line),用于连接外设与CPU的中断引脚(Interrupt Pin)。当外部设备发生状态改变时,可以通过这条信号线向处理器发出一个中断请求(Interrupt Request,IRQ),其中外部设备通常被称作中断源(Interrupt Source)。

        处理器一般只有两根左右的中断引脚(Cortex A9 中的 IRQ、FIQ),而管理的外设却很多。为了解决这个问题,现代设备的中断信号线并不是与处理器直接相连,而是与一个称为中断控制器的设备相连接,后者才跟处理器的中断引脚连接。中断控制器一般可以通过处理器进行编程配置,在Cortex A9 中称为通用中断控制器GIC(Gerneric Interrupt Controller)。下图是一个典型的中断硬件连接的系统框架图:


      这里的中断控制器是可编程中断控制器PIC(Programmable Interrupt Controller)。PIC的输出中断信号线连接到处理器的INT引脚上,这是处理器专门用来接收中断信号的pin脚。外部设备的中断线连接到PIC的pin引脚上,这是PIC用来接收外设中断的pin脚。比如第一个设备的中断线通过P0连到PIC上。在实际的硬件平台上,PIC有的在CPU外部,比如x86平台的8259中断控制器;有的被封装到CPU的内部,这广泛见于嵌入式领域。一颗SoC芯片内部集成了处理器和各种外部设备的控制器,其中包括PIC。

     IRQ相关信息管理的关键点是一个全局数组,每个数组项对应一个IRQ编号,软件中断号irq就是这个数组的索引,irq将一对一或多对一(共享)映射到硬件中断源编号。不同的操作系统相关数据结构的实现和映射策略实现可能有差别。

二、 中断向量表

         中断向量表其实是处理器内部的概念,因为处理器除了会被外部设备中断外,其内部也可能产生异常等事件,例如在MIPS中,中断只是异常的一种。当这些事件发生时,CPU必须暂停手头上的工作,转而去处理中断或异常,因此处理器需要知道到哪里去获得这些中断或异常的处理函数的目标地址。中断向量表就是用来解决这个问题,其中每一项都是一个中断或异常处理函数的入口地址,具体来说4个字节的函数指针将指向一段汇编微码(intConnectCode)执行跳转。

     外部设备的中断常常对应向量表中的某一项,这是通用框架的外部中断处理函数入口,因此在进入通用的中断处理函数之后,系统必须知道正在处理的中断是哪一个设备产生的,而这正是由软件中断号irq定的决。中断向量表的内容是由操作系统在初始化阶段来填写,对于外部中断,操作系统负责实现一个通用的外部中断处理函数,然后把这个函数的入口地址放到中断向量表中的对应位置。用户注册设备驱动ISR,实际上就是挂接到中断向量表中,覆盖某一项的默认处理实现特化。

中断向量表在linux\arch\arm\kernel\entry-armv.S

三、中断流程分析

1、当中断发生时会跳转到中断向量表处,就是上面那张表;

2、汇编处理部分不做分析:

table16个入口,只有2项有效,对应user mode、svc mode


Q:

1、handle_arch_irq 从哪来?

gic_init_bases  —> set_handle_irq(gic_handle_irq)—>  handle_arch_irq = handle_irq;

handle_arch_irq 实际执行的是gic_handle_irq

gic_handle_irq —> handle_IRQ —> generic_handle_irq()—> generic_handle_irq_desc—>desc —>handle_irq(irq, desc);

2、desc->handle_irq从哪来?

xxx_irq_type —>irq_set_handler(data->irq, handle_level_irq); —> __irq_set_handler —>

__irq_set_handler----- desc->handle_irq = handle;

handle_level_irq()

handle_irq_event —> handle_irq_event_percpu —>res = action->handler(irq, action->dev_id);真正调用到我们注册的中断处理函数


参考链接:https://blog.csdn.net/zqixiao_09/article/details/50908780

蜗窝科技有几篇文章写中断写得挺不错的 链接如下

1、linux kernel的中断子系统之(一)一个大概的软硬件框架。

2、linux kernel的中断子系统之(二):irq domain介绍。主要描述如何将一个HW interrupt ID转换成IRQ number。

3、linux kernel的中断子系统之(三):IRQ number和中断描述符。主要描述中断描述符相关的数据结构和接口API。

4、linux kernel的中断子系统之(四):high level irq event handler

5、linux kernel的中断子系统之(五):driver API。主要以一个普通的驱动程序为视角,看待linux interrupt subsystem提供的API,如何利用这些API,分配资源,是否资源,如何处理中断相关的同步问题等等。

6、linux kernel的中断子系统之(六):ARM中断处理过程,这份文档以ARM CPU为例,描述ARM相关的中断处理过程

7、linux kernel的中断子系统之(七):GIC代码分析,这份文档是以一个具体的interrupt controller为例,描述irq chip driver的代码构成情况。

8、linux kernel的中断子系统之(八):softirq

9、linux kernel的中断子系统之(九):tasklet

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容