参考:https://www.cnblogs.com/douzi2/p/5112743.html

当异常中断发生时,系统执行完当前指令后,将跳转到相应的异常中断处理程序处执行。在异常中断处理程序执行完成后,程序返回到发生中断的指令的下一条指令处执行。

说明

Kernel版本:4.14.111
ARM处理器,Contex-A7
在《ARM体系结构与编程》第9章中说到,ARM 中有个概念叫做“异常中断”,也就是包括外部中断在内的各种异常。显然,ARM体系的“异常中断”概念更加接近MIPS体系中的“异常”概念。

思考

  1. 首先中断向量是什么,中断向量表又是什么.
  2. ARM中异常中断的种类
  3. 向量中断和非向量中断
  4. 内核在哪里定义中断向量表
  5. 异常来时,中断跳转过程
  6. 为什么异常中断向量表必须设置在从0地址开始
  7. FIQ相比IRQ,FIQ称为快速中断,IRQ称为普通中断。FIQ为什么比IRQ快

1. 首先中断向量是什么,中断向量表又是什么

  • 中断向量
    中断服务程序的入口地址。在某些计算机中,中断向量的位置存放一条跳转到中断服务程序入口地址的跳转指令。
    来存放中断向量(共256个),称这一片内存区为中断向量表,地址范围是0~3FFH

  • 中断向量地址:
    存储中断向量的存储单元地址

  • 中断向量:
    中断向量的集合,按中断类型号从小到大的顺序存放到存储器的某一区域内,这个存放中断向量的存储区叫做中断向量表,即中断服务程序入口地址表。

CPU是根据中断号获取中断向量值,即对应中断服务程序的入口地址值。因此为了让CPU由中断号查找到对应的中断向量,就需要在内存中建立一张查询表

2. ARM中异常中断的种类

说明
Kernel版本:4.14.111
ARM处理器,Contex-A7
在《ARM体系结构与编程》第9章中说到,ARM 中有个概念叫做“异常中断”,也就是包括外部中断在内的各种异常。显然,ARM体系的“异常中断”概念更加接近MIPS体系中的“异常”概念。

ARM异常中断向量表

ARM的异常中断向量表可以是高端向量表,也可以是低端向量表,两者取其一。区别是基地址不同。高端向量是ARM架构可选配置,可以通过硬件外部输入管脚来配置是低端向量还是高端向量,不能通过指令来改变向量的位置,但如果ARM芯片内部有标准ARM协处理器,那么协处理器CP15的寄存器C1的bit13可以用来切换低端和高端向量地址,等于0时为低端向量,等于1时为高端向量。
  Linux内核分用户空间、内核空间,通常32位处理器,用户空间0-3G,内核空间3-4G,所以Linux内核使用高端向量表。

ARM的一个非常重要的寄存器——CPSR程序状态寄存器
cpsr这个寄存器用来设定进入哪种模式

3. 向量中断和非向量中断

向量中断 非向量中断
    向量者,矢量也,即指方向,门路。
    向量中断------由硬件提供中断服务程序入口地址;
    非向量中断------由软件件提供中断服务程序入口地址;

推荐向量中断就是不同的中断有不同的入口地址,非向量中断就只有一个入口地址,进去了再判断中断标志来识别具体是哪个中断。向量中断实时性好,非向量中断简单

  • 向量中断模式用于RESET、NMI(非屏蔽中断)、异常处理。当向量中断产生时,控制器直接将PC赋值,如跳到0x0000000d处,而在0x0000000d地址处通常放置ISR服务程序地址LDR PC, =ISR_HANDLER。——代码都是现成的
  • 非向量中断模式,有一个寄存器标识位,跳转到统一的函数地址,此函数通过判别寄存器标识位和优先级关系进行中断处理。向量中断模式是当CPU读取位于0x18处的IRQ中断指令的时候,系统自动读取对应于该中断源确定地址上的指令取代0x18处的指令,通过跳转指令系统就直接跳转到对应地址函数中,节省了中断处理时间提高了中断处理速度。例如 ADC 中断的向量地址为0xC0,则在0xC0处放如下代码:ldr PC,=HandlerADC 当ADC中断产生的时候系统会自动跳转到HandlerADC函数中处理中断。

对于ARM,ARM没有NMI,外部中断走的是非向量中断,即走注册的中断服务例程ISP,这个是驱动工程师自己注册的request_irq的irq_handle

但是无论是非向量中断还是向量中断,都会走到__vectors_start,然后根据中断类型走到对于的分类,例如IRQ就是走到了__vectors_start的vendor_irq,然后最终走到了irq_desc(中断例程描述符表),里面就定义了irqaction,irqaction链表指向了irq_handler_t中断服务程序,即reques_irq注册的中断服务程序

而对于其他类型的异常,如RESET,硬件已经做好了现成的处理code,根据向量表直接到达代码的地址,然后执行。区别于非向量的IRQ是统一的地址,进入这个地址后,再由中断例程描述符表区分中断

4. 内核在哪里定义中断向量表

在vmlinux.lds.S描述了__vectors_start的起始位置,从0xffff0000开始

 /** The vectors and stubs are relocatable code, and the* only thing that matters is their relative offsets*/__vectors_start = .;.vectors 0xffff0000 : AT(__vectors_start) {            (1)*(.vectors)}. = __vectors_start + SIZEOF(.vectors);__vectors_end = .;__stubs_start = .;.stubs ADDR(.vectors) + 0x1000 : AT(__stubs_start) {*(.stubs)                                        (2)}. = __stubs_start + SIZEOF(.stubs);__stubs_end = .;

1)链接时,将.vectors段内容链接到虚拟地址0xffff0000地址。(这里我理解为在vmlinux镜像中.vectors段连续,夹在__vectors_start和__vectors_end 中间,但是链接的虚拟地址指向0xffff0000)
2)同上。
  arch/arm/kernel/entry-armv.S 中.vectors段保存了异常向量表。

 .section .vectors, "ax", %progbits
.L__vectors_start:W(b)  vector_rstW(b)  vector_undW(ldr)    pc, .L__vectors_start + 0x1000W(b) vector_pabtW(b) vector_dabtW(b) vector_addrexcptnW(b)   vector_irqW(b)  vector_fiq

(软件中的向量表和硬件中的offset定义的一致性)

上面是中断向量表,而具体的向量是在vector_stub中,例如,vector_irq

/** Interrupt dispatcher*/vector_stub irq, IRQ_MODE, 4   .long   __irq_usr               @  0  (USR_26 / USR_32).long   __irq_invalid           @  1  (FIQ_26 / FIQ_32).long   __irq_invalid           @  2  (IRQ_26 / IRQ_32).long   __irq_svc               @  3  (SVC_26 / SVC_32).long   __irq_invalid           @  4.long   __irq_invalid           @  5.long   __irq_invalid           @  6.long   __irq_invalid           @  7.long   __irq_invalid           @  8.long   __irq_invalid           @  9.long   __irq_invalid           @  a.long   __irq_invalid           @  b.long   __irq_invalid           @  c.long   __irq_invalid           @  d.long   __irq_invalid           @  e.long   __irq_invalid           @  f

5. 异常来时,中断跳转过程

参考:http://blog.chinaunix.net/uid-29045944-id-3968667.html

根据异常向量表,有异常的时候就可以跳转了;但是跳到哪里呢?

上面说到,当有IRQ中断(我们在7种异常中断类型举IRQ为例)时,CPU根据中断向量表进入vector_irq,vector_irq的描述在vector_stub中,假如IRQ中断时在用户usr模式,则进入
__irq_usr,__irq_usr

__irq_usr:usr_entry    @保存中断上下文kuser_cmpxchg_checkirq_handler  @调用中断处理程序get_thread_info tsk @获取当前进程的进程描述符中的成员变量thread_info的地址,并将该地址保存到寄存器tsk(r9)(在entry-header.S中定义)mov    why, #0b    ret_to_user_from_irq @中断处理完成,恢复中断上下文并返回中断产生的位置UNWIND(.fnend     )
ENDPROC(__irq_usr)

如果发生中断前处于核心态则进入__irq_svc,其定义如下

__irq_svc:svc_entry  @保存中断上下文irq_handler  @调用中断处理程序#ifdef CONFIG_PREEMPTldr    r8, [tsk, #TI_PREEMPT]      @ get preempt countldr r0, [tsk, #TI_FLAGS]        @ get flagsteq r8, #0              @ if preempt count != 0movne  r0, #0              @ force flags to 0tst  r0, #_TIF_NEED_RESCHEDblne  svc_preempt  @如果不等于0,说明发生内核抢占,需要重新调度。
#endifsvc_exit r5, irq = 1         @恢复中断上下文UNWIND(.fnend      )
ENDPROC(__irq_svc)

上面代码中的usr_entry和svc_entry是一个宏定义,主要用于保护中断上下文到栈中
保存中断上下文后则进入中断处理程序——irq_handler,定义在arch/arm/kernel/entry_armv.S文件中:

#ifdef CONFIG_MULTI_IRQ_HANDLERldr   r1, =handle_arch_irqmov    r0, spbadr  lr, 9997fldr    pc, [r1]
#elsearch_irq_handler_default
#endif

这里32位走arch_irq_handler_default,64位走handle_arch_irq

(1 )arch_irq_handler_default:

arch_irq_handler_default ——>asm_do_IRQ()—>handle_IRQ()—>__handle_domain_irq()—>generic_handle_irq()—>调用用户request_irq注册的中断处理函数

(2)handle_arch_irq :
handle_arch_irq---->set_handle_irq---->gic_handle_irq---->handle_domain_irq()---->__handle_domain_irq()---->generic_handle_irq()---->generic_handle_irq_desc()

2种最终都调用到了desc->handle_irq(desc)

(kernel/include/linux/irqdesc.h)
static inline void generic_handle_irq_desc(struct irq_desc *desc)
{desc->handle_irq(desc);  // 这里真正调用到,用户request_irq注册的中断处理函数
}

generic_handle_irq_desc调用中断描述符的handle_irq回调函数。对于不同的中断类型,handle_irq回调函数可能是handle_simple_irq、handle_level_irq、handle_fasteoi_irq、handle_edge_irq、handle_edge_eoi_irq、handle_percpu_irq。

假如我们用reques_irq注册的是gpio电平触发中断,那么这里的desc->handle_irq就是handle_level_irq。(省略转换流程)

最终,handle_level_irq----> handle_irq_event(desc)-----> handle_irq_event_percpu------> res = action->handler(irq, action->dev_id); /* 调用action->handler,即request_irq 时注册的handler 函数 */

这里我们关注struct irq_desc,称为中断描述符,该数据结构保存了关于所有IRQ的中断描述符信息

struct irq_desc {irq_flow_handler_t       handle_irq;  //指向中断函数, 中断产生后,就会执行这个handle_irqstruct irq_chip   *chip; //指向irq_chip结构体,用于底层的硬件访问,下面会介绍struct msi_desc             *msi_desc; void                     *handler_data;  void                     *chip_data;struct irqaction     *action;      /* IRQ action list */   //action链表,用于中断处理函数unsigned int                  status;                  /* IRQ status */unsigned int                  depth;                  /* nested irq disables */unsigned int                  wake_depth;        /* nested wake enables */unsigned int                  irq_count;   /* For detecting broken IRQs */unsigned int                  irqs_unhandled;spinlock_t            lock;          ... ...const char            *name;              //产生中断的硬件名字
} ;

其中的成员*chip的结构体,用于底层的硬件访问, irq_chip类型如下:

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);   //开启中断源... ...int              (*set_type)(unsigned int irq, unsigned int flow_type);  //将对应的引脚设置为中断类型的引脚... ...
#ifdef CONFIG_IRQ_RELEASE_METHODvoid            (*release)(unsigned int irq, void *dev_id);       //释放中断服务函数
#endif};

其中的成员struct irqaction *action,主要是用来存用户注册的中断处理函数,
一个中断可以有多个处理函数 ,当一个中断有多个处理函数,说明这个是共享中断.
所谓共享中断就是一个中断的来源有很多,这些来源共享同一个引脚。
所以在irq_desc结构体中的action成员是个链表,以action为表头,若是一个以上的链表就是共享中断
irqaction结构定义如下:

struct irqaction {irq_handler_t handler;      //等于用户注册的中断处理函数,中断发生时就会运行这个中断处理函数unsigned long flags;         //中断标志,注册时设置,比如上升沿中断,下降沿中断等cpumask_t mask;           //中断掩码const char *name;          //中断名称,产生中断的硬件的名字void *dev_id;              //设备idstruct irqaction *next;        //指向下一个成员int irq;                    //中断号,struct proc_dir_entry *dir;    //指向IRQn相关的/proc/irq/};

上面3个结构体的关系如下图所示:

7. 为什么异常中断向量表必须设置在从0地址开始

由硬件电路决定,一般固定设置异常中断向量表在0地址开始,即复位异常触发的地址

8. FIQ相比IRQ,FIQ称为快速中断,IRQ称为普通中断。FIQ为什么比IRQ快

  1. 寄存器处理速度快。FIQ比IRQ有更多的banked寄存器,可以自动保存和恢复值。如果你FIQ中断处理程序足够用这几个独立的寄存器来运作,它就不会进行通用寄存器的压栈,这样也省了一些时间。
  2. FIQ比IRQ有更高的优先级,如果FIQ和IRQ同时产生,那么FIQ先处理。
  3. FIQ比IRQ至少少了一条跳转指令。FIQ的1C以后没有任何中断向量表了,这样可以直接在1C处放FIQ的中断处理程序,而IRQ的18只能放一条指令,然后跳转
  4. FIQ的响应比IRQ更快

linux内核学习10.1:Linux内核ARM7架构异常中断向量表相关推荐

  1. linux内核学习之三:linux中的32位与64位

    linux内核学习之三:linux中的"32位"与"64位" 在通用PC领域,不论是windows还是linux界,我们都会经常听到"32位" ...

  2. Linux内核ARM架构异常中断向量表

    Linux内核ARM架构异常中断向量表 说明 ARM中异常中断的种类 ARM异常中断向量表 内核异常向量表 异常向量表跳转 vector_srub宏 内核启动建立异常向量表   当异常中断发生时,系统 ...

  3. linux系统管理学习笔记之八---linux文件与目录的管理及权限

    linux系统管理学习笔记之八---linux文件与目录的管理及权限 2010-01-05 09:00:49 标签:权限 管理 文件目录 linx [推送到技术圈] 版权声明:原创作品,允许转载,转载 ...

  4. Linux+javaEE学习笔记之Linux网络环境配置

    Linux+javaEE学习笔记之Linux网络环境配置 网络知识简单介绍: Ip地址是:IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物 ...

  5. linux系统管理学习笔记之一-------linux解压缩命令

    linux系统管理学习笔记之一-------linux解压缩命令 2009-12-29 11:52:55 标签:linux tar [推送到技术圈] 版权声明:原创作品,允许转载,转载时请务必以超链接 ...

  6. linux内核学习6:Linux的CPU高速缓存cache和页高速缓存cache,buffer

    一.CPU高速缓存(cache) 参考:https://blog.csdn.net/u014470361/article/details/80060701 参考:https://blog.csdn.n ...

  7. 我的linux内核学习之路,Linux再学习(一)-学习路线规划

    摘要: ,一般可以通过-h查看help,就能找到相应的配置项还可以通过man命令,查看文档无论是什么命令行工具,最终的配置一般会落到一个文件上,只要找到了那个文件,文件中会有注释,也可以挨个儿看下去, ...

  8. 我的内核学习笔记14:内核设备树学习

    李迟按: 上一篇内核的文章是2年半前,期间因工作转行而停止研究,最近又重新捡起.这个系列从2013年起间断地更新,本来想从系统角度逐步写的,但工作量十分庞大,现在也想通了,在适合的时间写,不带目的,不 ...

  9. windows启动linux系统,windows 10 启动linux系统

    windows 10 启动linux系统 [2021-02-18 21:26:20]  简介: 系统运维 Windows中tocmat设置为服务开机启动一 安装Java,tomcat安装jdk,解压t ...

最新文章

  1. Redis源码解析——字典基本操作
  2. oracle merge
  3. oracle开放查询表权限_oracle 查询当前用户的表和其他用户的表
  4. 5.Hadoop的学习(Hadoop的配置(伪分布式的搭建)-1)
  5. HTML 4.01 event 事件
  6. Pytorch(一) --线性模型
  7. EntityFramework Core如何映射动态模型?
  8. java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring EventListener,超时处理和空循环性能优化...
  9. python软件下载安装-【Python下载】Python安装 v3.8.1 官方版-七喜软件园
  10. 软件工程毕业答辩常问的问题
  11. 记录一下Material Dialogs的使用
  12. windows上安装macos系统(超详细,可直接使用)
  13. 《九日集训》(第一讲)函数
  14. 总结 and 读后感之自控力 By 凯利·麦格尼格尔
  15. EXCEL表格-根据身份证号计算性别、年龄(IF、MOD函数)
  16. 53 张图详解防火墙的 55 个知识点
  17. 我是这样克服拖延症的
  18. 星座生辰八字算命系统超强大功能程序源码下载
  19. amd超频软件LINUX,AMD官方超频工具下载_AMD OverDrive 超频工具-PChome下载中心
  20. 微型计算机原理与接口技术ppt,单片机原理与接口技术课件ppt

热门文章

  1. MiniUI拖拽式列表示例
  2. oracle中all和any,Oracle中any和all的区别用法
  3. 管理者常犯的十个毛病(一)(余世维讲座文字整理版)
  4. 第二章:2.3 卷积定义(卷积积分与卷积和)
  5. ConvNeXt-Yolo5
  6. hdu 7092 仓颉造数 (猜测,手模数据找规律,推公式)
  7. 2021年数维杯数学建模B题中小城市地铁运营与建设优化设计求解全过程文档及程序
  8. 计算机中丢失crashrpt,crashrpt.dll
  9. 游戏数值策划属性篇(三):战斗公式的设计
  10. gan实战(基础GAN、DCGAN)