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

article/2025/9/13 22:06:13

参考: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更快

http://chatgpt.dhexx.cn/article/L3SexwVT.shtml

相关文章

汇编_修改中断向量表_自定义中断

文章目录 中断向量表介绍索引方式:修改中断向量表 自定义中断思路实现效果:实现代码执行步骤 中断向量表 介绍 在dos系统中,总共设计了256种中断,每个中断都有一个中断号,通过中断号来映射中断程序的地址。为了实现映…

9.2 中断向量表的结构

计算机组成 9 中断和异常 9.2 中断向量表的结构 我现在已经知道了,在运算的时候 一旦遇到了异常情况,就翻到第一页的第一行开始写的这些操作的指示,开始往下执行。开始往下执行,这就能解决问题了。但是问题在于这段操作&#xff0…

中断向量表的重印射

基于HAL库 以STM30F303RBT6为例: 打开system_stm32f3xx.c文件,修改其中的中断向量表位置,修改flash offset即可。 下载的位置也,也需要修改 做远程升级时,需要在单片机启动前运行一个boo程序,在boot程序中…

嵌入式杂谈之中断向量表

虽说接触了好久的单片机或者说嵌入式开发,不过对于有些概念还是比较模糊,因此此系列将会从一些零碎的小知识点出发,慢慢的遍历整张嵌入式开发的地图。 这次先来看一下中断向量表。 至于为什么会提到中断向量表,主要是因为我自己…

中断向量表--

中断向量表如何确定中断的入口地址? 每个中断对应一个中断号,比如一个中断事件的中断号为0x00,那么它对应的中断程序的入口地址就是0x0000-0x0003,另一个中断事件的中断号为0x01,那么它对应的中断程序的入口地址就是0…

一文了解Cortex-M中断向量表对齐原则

关注星标公众号,不错过精彩内容 来源 | 痞子衡嵌入式 一、Cortex-M中断向量表对齐原则 中断向量表就是一个集中保存系统全部中断处理函数(xxxIRQHandler)地址的常量数组(函数地址要占 4 个字节,因此数组中每个元素大小…

STM32中断向量表复制到SRAM中运行

1、生成Map文件 复制中断向量表前需要知道中断向量表的大小,可以通过编译生成的Map文件查看。 2、查看中断向量表大小 中断向量表的大小是固定的,与程序代码量无关,打开Map文件,直接搜索0x08000000,我使用的 MCU 是…

通过GDK8观察ARM框架下的中断向量表

一、中断向量表介绍 中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。 以上是中断在百度百科中些较为浅显的解释,中断实际…

【STM32】中断向量表

我是通过这个进行学习的,我觉得讲的很好,这里我稍加修改,作为自己的学习笔记: 嵌入式杂谈之中断向量表 前言 STM32根据boot引脚的配置方式有3种启动方式,但是无论哪一种方式,对于STM32来说都是从0x0000 0000启动 ST…

Cortex M4 中断向量表

前言 重新学习Cortex M4的中断向量表内容。中断向量表是Cortex M4自带的功能,厂家可以根据需要自行定义,所以需要学习该内容,直接看Cortex M4内核相关即可。我这查看的是《Cortex M3与M4权威指南.pdf》。 一、中断向量 中断向量是什么&…

中断向量表

关于中断向量的几点注释 1. 系统引导时,中断向量表放在内存何处? 系统刚引导时,内存0x00000到0x0003FF共1KB的空间用于存放中断向量表。每个中断向量占用4个字节,共可存储256个中断向量。 2. 系统引导时,处在实模式下,只可寻址1MB,为什么要用4个字节来寻址中断呢处理程…

TF_REPEATED_DATA ignoring data with redundant timestamp for frame left_wheel at time

使用urdf和xacro编写机器人仿真模型的时候,同时开启gazebo和rviz的时候遇到的问题 ubuntu20.04 ros noetic 开启gazebo并启动rviz时,rviz终端会一直产生一个警告:TF_REPEATED_DATA ignoring data with redundant timestamp for frame left…

ProtoBuf(Google Protocol Buffers)—— repeated 修饰字段注意点(packed修饰)

ProtoBuf—— (repeated 修饰 ) repeated1、定义2、使用事项 参考 repeated 1、定义 repeated类型相当于STL的vector,可以用来存放N个相同类型的内容。proto2 中还有 repeated 可选,在proto3 中则已经被摒弃并且默认格式为pack。…

Responder

在hack the box 最后一个sql注入题时,有这个工具的使用,之前没有接触过,看write up上是需要利用两个IP地址,似乎与内网渗透有关,马上查找相关资料,看到底是个什么东东。 什么是Responder 在攻防领域&…

Reverse

IDA中文不显示解决 参考链接 VScode中文乱码解决参考链接 (1 ~ 10攻防世界)(11~20为Buu,近乎为shift F12查找字符串) 1. xxxorrr 题目链接 题目WP 心得:可能会有其它隐藏函数对所需结果进行操作 2. SignIn RSA算法介绍 c是密文,m是明文…

java中@Repeatable的理解

Repeatable是jdk8中新增的注解,使用如Spring中的ComponentScan注解。在没有Repeatable注解的的注解中,在同一个地方使用相同的注解会报错,有了此元注解注解的注解,就可以在同一个地方使用相同的注解。其官方文档如下 The annotati…

TS:解决docker无法启动,报:start request repeated too quickly for docker.service错误-2021.12.08(已解决)

目录 文章目录 目录1、报错现象2、解决办法3、总结关于我最后 1、报错现象 首先,我当前的环境是:docker 20.10.11,vmworkstation虚机 很奇怪,自己的docker用着用着就出现问题了,查看服务发现起不来,重启docker服务也…

【已解决】TF_REPEATED_DATA ignoring data with redundant timestamp for frame

目录 1 问题背景2 问题探索3 问题解决4 告别Bug 1 问题背景 环境:Ubuntu20.04 ROS-noetic 现象:打开Rviz与Gazebo加载机器人模型时,终端不停刷新警告TF_REPEATED_DATA ignoring data with redundant timestamp for frame,且在未施…

已解决(一分钟)TF_REPEATED_DATA ignoring data with redundant timestamp for frame base_footprint at time解决方案

<1>问题描述 前言-仿真实验需求&#xff1a;获取小车的全局坐标系下的位姿信息&#xff0c;因此使用gmapping建图&#xff0c;并使用地图服务调用发布&#xff0c;自己写了个节点将map坐标系添加到现有的TF树上&#xff0c;最终TF树见最后截图。 进入正题&#xff0c;想…

【Protocol Buffer】Protocol Buffer入门教程(五):repeated限定修饰符

00. 目录 文章目录 00. 目录01. 限定修饰符介绍02. 字段API分析03. 测试代码04. 编译和测试05. 参考 01. 限定修饰符介绍 repeated 代表可重复&#xff0c;我们可以理解为数组 syntax "proto3";//指定版本信息&#xff0c;不指定会报错message Person //message为…