当前位置: 首页 > article >正文

异常与中断(上)

文章目录

  • 一、异常与中断的概念引入与处理流程
    • 1.1 生活中的中断
    • 1.2 母亲如何处理中断
    • 1.3 ARM系统中异常与中断处理流程
  • 二、ARM架构中异常与中断的处理
    • 2.1 处理流程
    • 2.2 cortex M3/M4
      • 2.2.1 M3/M4的向量表
      • 2.2.2 M3/M4的异常/中断处理流程
    • 2.3 cortex A7
      • 2.3.1 A7的向量表
      • 2.3.2 A7的异常/中断处理流程
  • 三、异常处理深入分析_保存现场
    • 3.1 回顾处理流程
    • 3.2 为什么要保存现场
    • 3.3 保存现场
    • 3.4 对于M3/M4
      • 2.4.1 硬件保存现场
      • 3.4.2 调用C函数
    • 3.5 对于A7

一、异常与中断的概念引入与处理流程

1.1 生活中的中断

在这里插入图片描述

假设有个大房间里面有小房间,婴儿正在睡觉,他的妈妈在外面看书。
问:这个母亲怎么才能知道这个小孩醒?

  1. 过一会打开一次房门,看婴儿是否睡醒,然后接着看书
  2. 一直等到婴儿发出声音以后再过去查看,期间都在读书

第一种方法叫做查询方式

  • 优点:简单
  • 缺点: 累

如何写程序?

while(1)
{
	1 read book(读书)
	2 open door(开门)
	  if(小孩还在睡)
		 return(继续读书)
	   else
		 照顾小孩	
}

第二种方法叫中断方式

  • 优点:不累
  • 缺点:复杂

如何写程序:

while(1)
{
	read book
}
中断服务程序() //核心问题:如何被调用?
{
	处理照顾小孩
}

1.2 母亲如何处理中断

我们还是看看母亲被小孩哭声打断如何照顾小孩?

母亲的处理过程

  • 平时看书
  • 发生了各种声音,如何处理这些声音
    • 有远处的猫叫(听而不闻,忽略)
    • 门铃声有快递(开门收快递)
    • 小孩哭声(打开房门,照顾小孩)
  • 母亲的处理
    • 只会处理门铃声和小孩哭声
      • 先在书中放入书签,合上书(保存现场)
      • 去处理 (调用对应的中断服务程序)
      • 继续看书(恢复现场)

不同情况,不同处理

  • 对于门铃:开门取快件
  • 对于哭声:照顾小孩

1.3 ARM系统中异常与中断处理流程

我们将母亲的处理过程抽象化:

  • 母亲的头脑相当于CPU
    • 耳朵听到声音会发送信号给脑袋
    • 声音来源有很多种
      • 有远处的猫叫,门铃声,小孩哭声
    • 这些声音传入耳朵,再由耳朵传给大脑
    • 除了这些可以中断母亲的看书,还有其他情况,比如:
      • 身体不舒服
      • 有只蜘蛛掉下来
      • 对于特殊情况无法回避,必须立即处理

对于arm系统,异常与中断的硬件框图如下:

在这里插入图片描述

所有的中断源(按键、定时器等),它们发出的中断汇聚到中断控制器
再由中断控制器发信号给CPU,告诉它发生了那些紧急情况。

除了这些中断,还有什么可以打断CPU的运行?

  • 指令不对
  • 数据访问有问题
  • reset信号
  • 等等,这些都可以打断断CPU,这些被称为异常
  • 中断属于一种异常

ARM系统中如何处理异常与中断?重点在于保存现场以及恢复现场
处理过程如下:

  • 保存现场(各种寄存器)
  • 处理异常(中断属于一种异常)
  • 恢复现场

细化一下,在ARM系统中如何使用异常(中断)?

  • 初始化

    • 设置中断源,让它可以产生中断
    • 设置中断控制器(可以屏蔽某个中断,优先级)
    • 设置CPU总开关,使能中断
  • 执行其他程序:正常程序

  • 产生中断,举例:按下按键—>中断控制器—>CPU

  • cpu每执行完一条指令都会检查有无中断/异常产生

  • 发现有中断/异常产生,开始处理:

    • 保存现场
    • 分辨异常/中断,调用对于异常/中断的处理函数
    • 恢复现场

不同的芯片,不同的架构,在这方面的处理稍有差别:

  • 保存/恢复现场:cortex M3/M4是硬件实现的,cortex A7是软件实现的
  • CPU中止当前执行,跳转去执行处理异常的代码:也有差异
    • cortex M3/M4在向量表上放置的是函数地址
    • cortex A7在向量表上放置的是跳转指令

二、ARM架构中异常与中断的处理

2.1 处理流程

  • 每执行完一条指令都会检查有无中断/异常产生
  • 发现有中断/异常产生,开始处理:
    • 保存现场
    • 分辨异常/中断,调用对应的异常/中断处理函数
    • 恢复现场

不同的芯片,不同的架构,在这方面的处理稍有差别:

  • CPU中止当前执行,跳转去执行处理异常的代码:也有差异

    • cortex M3/M4在向量表上放置的是函数地址
    • cortex A7在向量表上放置的是跳转指令
  • 保存/恢复现场:cortex M3/M4是硬件实现的,cortex A7是软件实现的

2.2 cortex M3/M4

参考资料:DDI0403E_B_armv7m_arm.pdfARM Cortex-M3与Cortex-M4权威指南.pdfPM0056.pdf

要想理解这个处理流程,需要从向量表说起。
向量,在数学定义里是有方向的量,在程序里可以认为向量就是一个数组,里面有多个项。
在ARM架构里,对于异常/中断,它们的处理入口会整齐地排放在一起。

2.2.1 M3/M4的向量表

M3/M4的向量表中,放置的是具体异常/中断的处理函数的地址。
比如发生Reset异常时,CPU就会从向量表里找到第1项,得到Reset_Handler函数的地址,跳转去执行。
比如发生EXTI Line 0中断时,CPU就会从向量表里找到第22项,得到EXTI0_IRQHandler函数的地址,跳转去执行。

  • 跳转之前,硬件会保存现场(会将寄存器保存在SP栈中)
  • 函数执行完毕,返回之后,硬件会恢复现场

在这里插入图片描述
编译程序时,我们会将异常向量表(途中的ROM向量表)先设置好并烧写在Flash上,CPU执行程序时,每执行完一条指令,都会判断是否有中断产生,如果有中断产生的话,则会终止下一条指令的运行,然后跳过去处理该异常/中断,他会首先保存现场,把当前程序所设计的寄存器保存到SP栈中,这是由硬件来实现的,保存到栈中后,还得去分辨一下发生的是哪个异常/中断,会使用异常号/中断号在异常向量表中找到对应项,接着CPU会跳过去执行里面的函数,该函数属于软件,函数执行完毕之后会返回,返回之后会触发硬件把栈中之前保存的内容恢复回去,这样之前被中断的程序就会开始继续运行。

在上图过程中,硬件帮我们做了大部分工作,我们只需要提供一个向量表中的C函数,给某一个异常/中断提供C函数即可。

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
                DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
                DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
                DCD     TIM8_UP_IRQHandler         ; TIM8 Update
                DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
                DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
                DCD     ADC3_IRQHandler            ; ADC3
                DCD     FSMC_IRQHandler            ; FSMC
                DCD     SDIO_IRQHandler            ; SDIO
                DCD     TIM5_IRQHandler            ; TIM5
                DCD     SPI3_IRQHandler            ; SPI3
                DCD     UART4_IRQHandler           ; UART4
                DCD     UART5_IRQHandler           ; UART5
                DCD     TIM6_IRQHandler            ; TIM6
                DCD     TIM7_IRQHandler            ; TIM7
                DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
                DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
                DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
                DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End

2.2.2 M3/M4的异常/中断处理流程

发生异常/中断时,硬件上实现了这些事情:

  • 保存现场:把被中断瞬间的寄存器的值保存进栈里

  • 根据异常/中断号,从向量表中得到函数地址,跳转过去执行

  • 函数执行完后,从栈中恢复现场

保存现场、分辨异常/中断、跳转执行,都是硬件实现的。
我们只需要在向量表中,把处理函数的地址填进去就可以了。

硬件承包了大部分的工作。

M3/M4的向量表中,存放的是函数地址

2.3 cortex A7

参考资料:ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf

实际上,以前的S3C2440属于ARM9处理器,它的异常/中断处理流程给cortex A7是一样的。

2.3.1 A7的向量表

A7的向量表中,放置的是某类异常的跳转指令
比如发生Reset异常时,CPU就会从向量表里找到第0项,得到b reset指令,执行后就跳转到reset函数。
比如发生任何的中断时,CPU就会从向量表里找到第6项,得到ldr pc, _irq指令,执行后就跳转到_irq函数。

  • 跳转之前,硬件只会保存CPSR寄存器
  • 跳转之后,软件要保存现场
  • 函数执行完毕,返回之前,软件恢复现场

在这里插入图片描述
编译程序时,我们会先设置好异常向量表,程序开始运行,当运行到CMP指令时发生中断,CPU终止BLEQ指令的运行,会先去处理中断(irq):CPU会切换进入irq模式(若发生异常则进入各类异常对应的模式),接着CPU去异常向量表中得到一条指令并去执行该指令(注意与M3/M4的区别:这里是指令,而M3/M4是函数地址),该指令会一条跳转指令,会跳转去执行某个函数,在函数中首先会保存现场,接着分辨中断源并去处理中断,最后是恢复现场。

对于A7来说,硬件上并没有做太多事情,硬件上只帮我们切换CPU模式,跳转过来执行异常向量表中的某条指令,剩下的保存现场、分辨中断源以及恢复现场都是软件做的。

_start: 
    b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq

2.3.2 A7的异常/中断处理流程

发生异常/中断时,硬件上实现了这些事情:

  • CPU切换到对应的异常模式,比如IRQ模式、未定义模式、SVC模式

  • 保存被中断时的CPSR到SPSR

    • CPSR:current program status register,当前程序状态寄存器
    • SRSR:saved program status register,保存的程序状态寄存器
  • 跳到这个异常的入口地址去,执行指令,这通常是一条跳转指令

软件要做的事情就比较多了:

  • 保存现场
  • 分辨异常/中断
  • 调用对应的处理函数
  • 恢复现场

A7的向量表中,存放的是跳转指令

三、异常处理深入分析_保存现场

3.1 回顾处理流程

CPU每执行完一条指令都会检查有无中断/异常产生,发现有中断/异常产生,开始处理:

  • 保存现场
  • 分辨异常/中断,调用对应的异常/中断处理函数
  • 恢复现场

对于不用的处理器,具体的处理工作有差别:

  • 保存现场:cortex M3/M4里是硬件完成,cortex A7等是软件实现
  • 分辨异常/中断:cortex M3/M4里是硬件完成,cortex A7等是软件实现
  • 调用处理函数:cortex M3/M4里是硬件来调用,cortex A7等是软件自己去调用
  • 恢复现场:cortex M3/M4里是软件触发、硬件实现,cortex A7等是软件实现

不管是硬件还是软件实现,第一步都是保存现场

3.2 为什么要保存现场

1

任何程序,最终都会转换为机器码,上述C代码可以转换为右边的汇编指令。
对于这4条指令,它们可能随时被异常打断,怎么保证异常处理完后,被打断的程序还能正确运行?

  • 这4条指令涉及R0、R1寄存器,程序被打断时、恢复运行时,R0、R1要保持不变

  • 执行完第3条指令时,比较结果保存在程序状态寄存器里,程序被打断时、恢复运行时,程序状态寄存器保持不变

  • 这4条指令,读取a、b内存,程序被打断时、恢复运行时,a、b内存保持不变

内存保持不变,这很容易实现,程序不越界就可以。
所以,关键在于R0、R1、程序状态寄存器要保持不变(当然不止这些寄存器):

  • 在处理异常前,把这些寄存器保存在栈中,这称为保存现场
  • 在处理完异常后,从栈中恢复这些寄存器,这称为恢复现场

3.3 保存现场

ARM处理器中有这些寄存器:
在这里插入图片描述

在arm中有个ATPCS规则(ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)。
约定R0-R15寄存器的用途:

  • R0-R3

    调用者和被调用者之间传参数

  • R4-R11

    函数可能被使用,所以在函数的入口保存它们,在函数的出口恢复它们。

在这里插入图片描述

还有一个程序状态寄存器,对于M3/M4它被称为XPSR,对于A7它被称为CPSR,我们简称为PSR。
R0-R15、PSR,就是所谓的现场
发生异常/中断后,在处理异常/中断前,需要保存现场,难道需要保存所有这些寄存器吗?
不需要!
在C函数中,可以修改R0-R3、R12、R14(LR)以及PSR。如果C函数要用到这些寄存器,就要把它们保存到栈里,在函数结束前在从栈中恢复它们。
这些寄存器被拆分成2部分:调用者保存的寄存器(R0-R3,R12,LR,PSR)被调用者保存的寄存器(R4-R11)
比如函数A调用函数B,函数A应该知道:

  • R0-R3是用来传参数给函数B的
  • 函数B可以肆意修改R0-R3
  • 函数A不要指望函数B帮你保存R0-R3
  • 保存R0-R3,是函数A的事情
  • 对于LR、PSR也是同样的道理,保存它们是函数A的责任

对于函数B:

  • 我用到R4-R11中的某一个,我都会在函数入口保存、在函数返回前恢复
  • 保证在B函数调用前后,函数A看到的R4-R11保存不变

假设函数B就是异常/中断处理函数,函数B本身能保证R4-R11不变,那么保存现场时,只需要保存这些:

  • 调用者保存的寄存器(R0-R3,R12,LR,PSR)
  • PC

3.4 对于M3/M4

参考资料:DDI0403E_B_armv7m_arm.pdfARM Cortex-M3与Cortex-M4权威指南.pdfPM0056.pdf

2.4.1 硬件保存现场

在这里插入图片描述

3.4.2 调用C函数

C函数执行完后,它返回LR所指示的位置。
难道把LR设置为被中断的程序的地址就行了吗?
如果只是返回LR所指示的地方,硬件帮我们保存在栈里的寄存器,怎么恢复?
M3/M4在调用异常处理函数前,把LR设置为一个特殊的值,转给特殊的值被称为EXC_RETURN
当PC寄存器的值等于EXC_RETURN时,会触发异常返回机制,简单地说:会从栈里恢复R0-R3,R12,LR,PC,PSR等寄存器。
EXC_RETURN的值,请参考ARM Cortex-M3与Cortex-M4权威指南.pdf,截图如下:

在这里插入图片描述

在这里插入图片描述

以前我们调用C函数时,会设置LR=返回地址,如果这样设置,它确实可以在执行完该函数后重新回到原位置执行,但是它无法把保存在栈中的寄存器恢复出来,此时对于M3/M4会进行一个操作:它把LR设置为一个特殊值,像往常一样去调用中异常/中断处理函数,当函数执行完毕后,会跳到LR指示的地方尝试去返回,此时CPU会发现LR是一个特殊值,于是会触发恢复现场,则会把栈中的值恢复到寄存器中,比如把返回地址恢复到PC寄存器中。

补充2个知识点:

  • 操作模式:M3/M4有两个操作模式

    • 处理模式:执行中断服务程序等异常处理时,处于处理模式
    • 线程模式:执行普通应用程序代码时,处于线程模式
  • M3/M4有连个SP寄存器:SP_process、SP_main

    • 有些RTOS在运行用户程序时会使用SP_process,默认使用SP_main。

3.5 对于A7

它寄存器如下:

在这里插入图片描述

处理器有9中模式:User、Sys、FIQ、IRQ、ABT、SVC、UND、MON、HYP。
上图中深色的寄存器,表示该模式下的"Banked"寄存器,比如SPSR寄存器,在很多模式下都有自己的、单独的寄存器。
比如IRQ模式下访问SPSR时,访问到的是IRQ模式下自己的SPSR_irq,别的模式下无法访问SPSR_irq。

比较值得关注的是FIQ模式,名为"快中断",它有很多"Banked"寄存器:R8-R12,SP,LR。
在FIQ模式下,它既然能使用自己的R8-R12,SP,LR,自然不需要去保存被中断的程序的"R8-R12,SP,LR"了。
省去保存这几个寄存器的时间,处理中断时自然就快很多,所以被称为"FIQ"。

从上图也看到,几乎每个模式下都有自己是SP寄存器,意味着这些模式下有自己的栈。

当发生异常时,以IRQ为例:

  • CPU会自动切换进入对应的模式,比如进入IRQ模式
  • 并且会把被中断是的CPSR保存到SPSR_irq里

所以发生异常/中断时,在保存现场时,只需要保存:

  • 调用者保存的寄存器(R0-R3,R12,LR)
  • PC

在这里插入图片描述

当前程序正处于User(用户)或Sys(系统)模式下,此时正在执行第一条xxxx指令,执行完该指令后产生中断/异常,于是在硬件上会发生几件事:1.CPU强制进入IRQ模式,所谓IRQ模式就是以后程序使用SP时,用的就是IRQ模式里面的SP_IRQ的栈;2.当前程序处于User或Sys模式运行,那么就是用的对应下面程序状态寄存器(PSR),里面保存有比较的结果(前面的比较例子),此时该寄存器也会强制保存在IRQ模式下的SPSR_IRQ;3、跳转到IRQ入口执行异常向量表中的某条指令,比如跳过去PC执行_iqr函数。这部分主要是硬件做的事,其中主要做的就是把程序状态寄存器保存起来

对于软件部分需要做以下几件事:1.保存现场。在前面可以的图可以看到,对于A7需要保存R0-R3,R12、LR寄存器,将以上几个寄存器保存进SP_IRQ中,图中还有PSR寄存器,但其已经被硬件保存起来;2.分辨、处理异常/中断;3.恢复现场。


http://www.kler.cn/a/463233.html

相关文章:

  • Niushop商城商业插件_cps联盟_包装转换_视频购物_同城配送_上门预约等插件的安装方法
  • 【paddle】初次尝试
  • 【蓝桥杯比赛-C++组-经典题目汇总】
  • 《Java 数据结构》
  • iOS 11 中的 HEIF 图像格式 - 您需要了解的内容
  • 【深度学习-降维篇】t-SNE:让高维数据“看得见”的降维利器
  • C++设计模式:状态模式(自动售货机)
  • HIVE函数使用案例之----行列转换
  • nginx学习之路-nginx配置https服务器
  • 17爬虫:关于DrissionPage相关内容的学习01
  • 大模型—Ollama将Python函数作为参数传递,增强函数调用功能
  • shell脚本的【算数运算、分支结构、test表达式】
  • PHP:IntelliJ IDEA 配置 PHP 开发环境及导入PHP项目
  • OpenCV 特征检测和特征匹配方法汇总
  • 如何使用大语言模型进行事件抽取与关系抽取
  • smolagents:一个用于构建代理的简单库
  • SpringBoot教程(三十二) SpringBoot集成Skywalking链路跟踪
  • hhdb客户端介绍(65)
  • HT-HaiBOX边缘计算盒 智慧工厂方案,智慧医疗方案,智慧加油站方案,智慧安防方案,智慧城市方案;方案定制开发
  • Python编程实现“天天酷跑”小游戏(源码附上)
  • 基于单片机的野营自动感应灯系统(论文+源码)
  • 使用Wikitext2数据集对Llama-7B和Llama3-8B模型进行50%权重剪枝的一般步骤和可能的实现方式
  • 大数据技术-Hadoop(三)Mapreduce的介绍与使用
  • 【springboot yml配置】多环境切换
  • 使用 Certbot 快速为 hustoj 申请并自动配置免费 SSL 证书 自动续期
  • 在Ubuntu下通过Docker部署Mastodon服务器