鸿蒙内核源码分析(异常接管篇) | 社会很单纯,复杂的是人

为何要有异常接管?

拿小孩成长打比方,大人总希望孩子能健康成长,但在成长过程中总会遇到各种各样的问题,树欲静而风不止,成长路上有危险,有时是自己的问题有时是外在环境问题.就像抖音最近的流行口水歌一样,社会很单纯,复杂的是人啊,每次听到都想站起来扭几下.哎! 老衲到底做错什么了?

比如:老被其他小朋友欺负怎么弄? 发现乱花钱怎么搞? 青春期发育怎么应对? 失恋要跳楼又怎么办? 意思是超过他的认知范围,靠它自己解决不了了,就需要有更高权限,更高智慧的人介入进来,帮着解决,干擦屁股的事.

那么应用程序就是那个小孩,内核就是监护人,有更高的权限,更高的智慧.而且监护人还不止一个,而是六个,每个监护人对应解决一种情况,情况发生了就由它来接管这件事的处理,小朋友你就别管了哈,先把你关家里,处理好了外面安全了再把应用程序放出来玩去.

这六个人处理问题都自带工具,有标准的解决方案,有自己独立的办公场所,办公场所就是栈空间(独立的),标准解决方案就是私有代码段,放在固定的位置.而自带的工具就是 SPSR_***SP_***LR_***寄存器组.详见 系列篇之工作模式篇 ,这里再简单回顾下有哪些工作模式,包括小孩自己(用户模式)一共是七种模式.

七种工作模式

图来源于 ARM720T.pdf 第43页,在ARM体系中,CPU工作在以下七种模式中:

  • 用户模式(usr):该模式是用户程序的工作模式,它运行在操作系统的用户态,它没有权限去操作其它硬件资源,只能执行处理自己的数据,也不能切换到其它模式下,要想访问硬件资源或切换到其它模式只能通过软中断或产生异常。

  • 快速中断模式(fiq):快速中断模式是相对一般中断模式而言的,用来处理高优先级中断的模式,处理对时间要求比较紧急的中断请求,主要用于高速数据传输及通道处理中。

  • 普通中断模式(irq):一般中断模式也叫普通中断模式,用于处理一般的中断请求,通常在硬件产生中断信号之后自动进入该模式,该模式可以自由访问系统硬件资源。

  • 管理模式(svc):操作系统保护模式,CPU上电复位和当应用程序执行 SVC 指令调用系统服务时也会进入此模式,操作系统内核的普通代码通常工作在这个模式下。

  • 终止模式(abt):当数据或指令预取终止时进入该模式,中止模式用于支持虚拟内存或存储器保护,当用户程序访问非法地址,没有权限读取的内存地址时,会进入该模式,

  • 系统模式(sys):供操作系统使用的高特权用户模式,与用户模式类似,但具有可以直接切换到其他模式等特权,用户模式与系统模式两者使用相同的寄存器,都没有SPSR(Saved Program Statement Register,已保存程序状态寄存器),但系统模式比用户模式有更高的权限,可以访问所有系统资源。

  • 未定义模式(und):未定义模式用于支持硬件协处理器的软件仿真,CPU在指令的译码阶段不能识别该指令操作时,会进入未定义模式。

除用户模式外,其余6种工作模式都属于特权模式

  • 特权模式中除了系统模式以外的其余5种模式称为异常模式
  • 大多数程序运行于用户模式
  • 进入特权模式是为了处理中断、异常、或者访问被保护的系统资源
  • 硬件权限级别:系统模式 > 异常模式 > 用户模式
  • 快中断(fiq)与慢中断(irq)区别:快中断处理时禁止中断

每种模式都有自己独立的入口和独立的运行栈空间. 系列篇之CPU篇 已介绍过只要提供了入口函数和运行空间,CPU就可以干活了.入口函数解决了指令来源问题,运行空间解决了指令的运行场地问题.
而且在多核情况下,每个CPU核的每种特权模式都有自己独立的栈空间.注意是特权模式下的栈空间,用户模式的栈空间是由用户(应用)程序提供的.

官方概念

异常接管是操作系统对运行期间发生的异常情况(芯片硬件异常)进行处理的一系列动作,例如打印异常发生时当前函数的调用栈信息、CPU现场信息、任务的堆栈情况等。
异常接管作为一种调测手段,可以在系统发生异常时给用户提供有用的异常信息,譬如异常类型、发生异常时的系统状态等,方便用户定位分析问题。

鸿蒙的异常接管,在系统发生异常时的处理动作为:显示异常发生时正在运行的任务信息(包括任务名、任务号、堆栈大小等),以及CPU现场等信息。

进入和退出异常方式

异常接管切换需要处理好两件事:

  • 一个是代码要切到哪个位置,也就是要重置PC寄存器,每种异常模式下的切换方式如图:

  • 另一个是要恢复每种模式的状态,即 CPSR(1个)SPSR(共5个) 的关系,对M[4:0]的修改,如图:

以下是M[4:0]在每种模式下具体操作方式:

栈帧

每个函数都有自己的栈空间,称为栈帧。调用函数时,会创建子函数的栈帧,同时将函数入参、局部变量、寄存器入栈。栈帧从高地址向低地址生长,也就是说栈底是高地址,栈顶是底地址. 详见 系列篇之用栈方式篇

ARM32 CPU架构为例,每个栈帧中都会保存PCLRSPFP寄存器的历史值。
堆栈分析原理如下图所示,实际堆栈信息根据不同CPU架构有所差异,此处仅做示意。
图中不同颜色的寄存器表示不同的函数。可以看到函数调用过程中,寄存器的保存。通过FP寄存器,栈回溯到异常函数的父函数,继续按照规律对栈进行解析,推出函数调用关系,方便用户定位问题。

解读

  • LR寄存器(Link Register),链接寄存器,指向函数的返回地址。

  • R11:可以用作通用寄存器,在开启特定编译选项时可以用作帧指针寄存器FP,用来实现栈回溯功能。
    GNU编译器(gcc)默认将R11作为存储变量的通用寄存器,因而默认情况下无法使用FP的栈回溯功能。为支持调用栈解析功能,需要在编译参数中添加-fno-omit-frame-pointer选项,提示编译器将R11作为FP使用。

  • FP寄存器(Frame Point),帧指针寄存器,指向当前函数的父函数的栈帧起始地址。利用该寄存器可以得到父函数的栈帧,从栈帧中获取父函数的FP,就可以得到祖父函数的栈帧,以此类推,可以追溯程序调用栈,得到函数间的调用关系。
    当系统发生异常时,系统打印异常函数的栈帧中保存的寄存器内容,以及父函数、祖父函数的栈帧中的LR、FP寄存器内容,用户就可以据此追溯函数间的调用关系,定位异常原因。

六种异常模式实现代码

/* Define exception type ID */		//ARM处理器一共有7种工作模式,除了用户和系统模式其余都叫异常工作模式
#define OS_EXCEPT_RESET          0x00	//重置功能,例如:开机就进入CPSR_SVC_MODE模式
#define OS_EXCEPT_UNDEF_INSTR    0x01	//未定义的异常,就是others
#define OS_EXCEPT_SWI            0x02	//软中断
#define OS_EXCEPT_PREFETCH_ABORT 0x03	//预取异常(取指异常), 指令三步骤: 取指,译码,执行, 
#define OS_EXCEPT_DATA_ABORT     0x04	//数据异常
#define OS_EXCEPT_FIQ            0x05	//快中断异常
#define OS_EXCEPT_ADDR_ABORT     0x06	//地址异常
#define OS_EXCEPT_IRQ            0x07	//普通中断异常

地址异常处理(Address abort)

@ Description: Address abort exception handler
_osExceptAddrAbortHdl: @地址异常处理SUB     LR, LR, #8                                       @ LR offset to return from this exception: -8.STMFD   SP, {R0-R7}                                      @ Push working registers, but don`t change SP.MOV     R0, #OS_EXCEPT_ADDR_ABORT                        @ Set exception ID to OS_EXCEPT_ADDR_ABORT.B       _osExceptDispatch                                @跳到异常分发统一处理

快中断处理(fiq)

@ Description: Fast interrupt request exception handler
_osExceptFiqHdl: @快中断异常处理SUB     LR, LR, #4                                       @ LR offset to return from this exception: -4.STMFD   SP, {R0-R7}                                      @ Push working registers.MOV     R0, #OS_EXCEPT_FIQ                               @ Set exception ID to OS_EXCEPT_FIQ.B       _osExceptDispatch                                @ Branch to global exception handler.

解读

  • 快中断处理时需禁用普通中断

取指异常(Prefectch abort)

@ Description: Prefectch abort exception handler
_osExceptPrefetchAbortHdl:
#ifdef LOSCFG_GDB
#if __LINUX_ARM_ARCH__ >= 7GDB_HANDLE OsPrefetchAbortExcHandleEntry
#endif
#elseSUB     LR, LR, #4                                       @ LR offset to return from this exception: -4.STMFD   SP, {R0-R7}                                      @ Push working registers, but don`t change SP.MOV     R5, LRMRS     R1, SPSRMOV     R0, #OS_EXCEPT_PREFETCH_ABORT                    @ Set exception ID to OS_EXCEPT_PREFETCH_ABORT.AND     R4, R1, #CPSR_MASK_MODE                          @ Interrupted modeCMP     R4, #CPSR_USER_MODE                              @ User modeBEQ     _osExcPageFault                                   @ Branch if user mode_osKernelExceptPrefetchAbortHdl:MOV     LR, R5B       _osExceptDispatch                                @ Branch to global exception handler.
#endif

数据访问异常(Data abort)

@ Description: Data abort exception handler
_osExceptDataAbortHdl: @数据异常处理,缺页就属于数据异常
#ifdef LOSCFG_GDB
#if __LINUX_ARM_ARCH__ >= 7GDB_HANDLE OsDataAbortExcHandleEntry
#endif
#elseSUB     LR, LR, #8                                       @ LR offset to return from this exception: -8.STMFD   SP, {R0-R7}                                      @ Push working registers, but don`t change SP.MOV     R5, LRMRS     R1, SPSRMOV     R0, #OS_EXCEPT_DATA_ABORT                        @ Set exception ID to OS_EXCEPT_DATA_ABORT.B     _osExcPageFault   @跳到缺页异常处理
#endif

软中断处理(swi)

@ Description: Software interrupt exception handler
_osExceptSwiHdl: @软中断异常处理SUB     SP, SP, #(4 * 16)	@先申请16个栈空间用于处理本次软中断STMIA   SP, {R0-R12}		@保存R0-R12寄存器值MRS     R3, SPSR			@读取本模式下的SPSR值MOV     R4, LR				@保存回跳寄存器LRAND     R1, R3, #CPSR_MASK_MODE                          @ Interrupted mode 获取中断模式CMP     R1, #CPSR_USER_MODE                              @ User mode	是否为用户模式BNE     OsKernelSVCHandler                               @ Branch if not user mode 非用户模式下跳转@ 当为用户模式时,获取SP和LR寄出去值@ we enter from user mode, we need get the values of  USER mode r13(sp) and r14(lr).@ stmia with ^ will return the user mode registers (provided that r15 is not in the register list).MOV     R0, SP											 @获取SP值,R0将作为OsArmA32SyscallHandle的参数STMFD   SP!, {R3}                                        @ Save the CPSR 入栈保存CPSR值ADD     R3, SP, #(4 * 17)                                @ Offset to pc/cpsr storage 跳到PC/CPSR存储位置STMFD   R3!, {R4}                                        @ Save the CPSR and r15(pc) 保存LR寄存器STMFD   R3, {R13, R14}^                                  @ Save user mode r13(sp) and r14(lr) 保存用户模式下的SP和LR寄存器SUB     SP, SP, #4PUSH_FPU_REGS R1	@保存中断模式(用户模式模式)											MOV     FP, #0                                           @ Init frame pointerCPSIE   I	@开中断,表明在系统调用期间可响应中断BLX     OsArmA32SyscallHandle	/*交给C语言处理系统调用*/CPSID   I	@执行后续指令前必须先关中断POP_FPU_REGS R1											 @弹出FP值给R1ADD     SP, SP,#4										 @ 定位到保存旧SPSR值的位置LDMFD   SP!, {R3}                                        @ Fetch the return SPSR 弹出旧SPSR值MSR     SPSR_cxsf, R3                                    @ Set the return mode SPSR 恢复该模式下的SPSR值@ we are leaving to user mode, we need to restore the values of USER mode r13(sp) and r14(lr).@ ldmia with ^ will return the user mode registers (provided that r15 is not in the register list)LDMFD   SP!, {R0-R12}									 @恢复R0-R12寄存器LDMFD   SP, {R13, R14}^                                  @ Restore user mode R13/R14 恢复用户模式的R13/R14寄存器ADD     SP, SP, #(2 * 4)								 @定位到保存旧PC值的位置LDMFD   SP!, {PC}^                                       @ Return to user 切回用户模式运行

普通中断处理(irq)

OsIrqHandler:	@硬中断处理,此时已切换到硬中断栈SUB     LR, LR, #4/* push r0-r3 to irq stack */STMFD   SP, {R0-R3}		@r0-r3寄存器入 irq 栈SUB     R0, SP, #(4 * 4)@r0 = sp - 16MRS     R1, SPSR		@获取程序状态控制寄存器MOV     R2, LR			@r2=lr/* disable irq, switch to svc mode */@超级用户模式(SVC 模式),主要用于 SWI(软件中断)和 OS(操作系统)。CPSID   i, #0x13				@切换到SVC模式,此处一切换,后续指令将入SVC的栈@CPSID i为关中断指令,对应的是CPSIE/* push spsr and pc in svc stack */STMFD   SP!, {R1, R2} @实际是将 SPSR,和LR入栈,入栈顺序为 R1,R2,SP自增STMFD   SP, {LR}	  @LR再入栈,SP不自增AND     R3, R1, #CPSR_MASK_MODE	@获取CPU的运行模式CMP     R3, #CPSR_USER_MODE		@中断是否发生在用户模式BNE     OsIrqFromKernel			@中断不发生在用户模式下则跳转到OsIrqFromKernel/* push user sp, lr in svc stack */STMFD   SP, {R13, R14}^ 		@sp和LR入svc栈

解读

  • 普通中断处理时可以响应快中断

未定义异常处理(undef)

@ Description: Undefined instruction exception handler
_osExceptUndefInstrHdl:@出现未定义的指令处理
#ifdef LOSCFG_GDBGDB_HANDLE OsUndefIncExcHandleEntry
#else@ LR offset to return from this exception:  0.STMFD   SP, {R0-R7}                                       @ Push working registers, but don`t change SP.MOV     R0, #OS_EXCEPT_UNDEF_INSTR                        @ Set exception ID to OS_EXCEPT_UNDEF_INSTR.B       _osExceptDispatch                                 @ Branch to global exception handler.#endif

异常分发统一处理

_osExceptDispatch: @异常模式统一分发处理MRS     R2, SPSR                                         @ Save CPSR before exception.MOV     R1, LR                                           @ Save PC before exception.SUB     R3, SP, #(8 * 4)                                 @ Save the start address of working registers.MSR     CPSR_c, #(CPSR_INT_DISABLE | CPSR_SVC_MODE)      @ Switch to SVC mode, and disable all interruptsMOV     R5, SPEXC_SP_SET __exc_stack_top, OS_EXC_STACK_SIZE, R6, R7STMFD   SP!, {R1}                                        @ Push Exception PCSTMFD   SP!, {LR}                                        @ Push SVC LRSTMFD   SP!, {R5}                                        @ Push SVC SPSTMFD   SP!, {R8-R12}                                    @ Push original R12-R8,LDMFD   R3!, {R4-R11}                                    @ Move original R7-R0 from exception stack to original stack.STMFD   SP!, {R4-R11}STMFD   SP!, {R2}                                        @ Push task`s CPSR (i.e. exception SPSR).CMP     R0, #OS_EXCEPT_DATA_ABORT 		@是数据异常吗?BNE     1f 								@不是跳到 锚点1处MRC     P15, 0, R8, C6, C0, 0 			@R8=C6(内存失效的地址) 0(访问数据失效)MRC     P15, 0, R9, C5, C0, 0 			@R9=C5(内存失效的状态) 0(无效整个指令cache)B       3f 								@跳到锚点3处执行
1:  CMP     R0, #OS_EXCEPT_PREFETCH_ABORT 	@是预取异常吗?BNE     2f 								@不是跳到 锚点2处MRC     P15, 0, R8, C6, C0, 2 			@R8=C6(内存失效的地址) 2(访问指令失效)MRC     P15, 0, R9, C5, C0, 1 			@R9=C5(内存失效的状态) 1(虚拟地址)B       3f 								@跳到锚点3处执行
2:  MOV     R8, #0MOV     R9, #03:  AND     R2, R2, #CPSR_MASK_MODE CMP     R2, #CPSR_USER_MODE                              @ User modeBNE     4f @不是用户模式STMFD   SP, {R13, R14}^                                  @ save user mode sp and lr
4:SUB     SP, SP, #(4 * 2) @sp=sp-(4*2)

非常重要的ARM37个寄存器

详见 系列篇之寄存器篇


http://www.ppmy.cn/news/1515781.html

相关文章

centos7安装Kafka单节点环境部署一-ZooKeeper安装与配置

由于Kafka运行需要zookeeper配合,zookeeper需要运行在JVM上,所以需要安装JDK,zookeeper。Kafka 从2.0.0版本开始就不再支持 JDK7 及以下版本,就以 CentOS 7 64位 JDK8 为例 1、下载ZooKeeper wget https://archive.apache.org/d…

探索C#的随机森林:内置随机数生成器的深度指南

标题:探索C#的随机森林:内置随机数生成器的深度指南 在编程世界中,随机数生成器是一种常用工具,它在模拟、游戏开发、加密、科学计算等领域发挥着重要作用。C#提供了强大的内置随机数生成器,使得生成随机数变得简单而…

现有electron-quick-start把vue项目打包后的dist打包exe自定义最小化点击事件

1.preload.js里暴露接口 const { contextBridge, ipcRenderer } require(electron) contextBridge.exposeInMainWorld(electronAPI, {WindowMin: (data) > {ipcRenderer.send(window-min, data);} });2.vue文件处理 if(window.electronAPI){window.electronAPI.WindowMi…

Hive SQL语言

目录 Hive SQL之数据库与建库 create database :创建数据库 use database :选择特定的数据库 drop database :删除数据库 Hive SQL之表与建表 create table : 创建表 分隔符指定语法 Hive SQL-DML-Load加载数据 Load语法功能 语法规则之filepath 语法规则之LOCAL …

IPython 使用技巧整理

IPython 是一个增强版的 Python 交互式解释器,它提供了许多有用的功能,比如自动补全、代码历史、多行编辑、魔术命令等。 1. 自动补全功能 IPython 的自动补全功能可以大大提高编码效率。当你在编写代码时,只需按下 Tab 键,IPyt…

【逐行注释】基于CV/CT模型的IMM|MATLAB程序|源代码复制后即可运行,无需下载

订阅专栏后可以直接查看完整的源代码(和注释),无需付费下载或其他的操作。代码复制到MATLAB上面可以得到和我一样的运行结果。 文章目录 程序概述完整代码与逐行注释运行结果解释按模块分析代码程序概述 基于EKF的多模型交互。以CV和CT两个模型进行交互,这里对代码进行逐…

虚拟内存和linux(操作系统part1)

一个操作系统的虚拟内存和linux部分知识点的笔记整理,资料大多参考于:小林coding和Javaguide。 虚拟内存的作用 第一,虚拟内存可以使得进程运行内存超过物理内存大小,因为程序运行符合局部性原理,CPU 访问内存会有很…

圈子论坛小程序搭建教程,系统快速部署上线指南,支持文章、源码、链接等上传

圈子论坛小程序是一种基于移动端的社交平台,旨在为用户提供交流分享、互动沟通的空间。以下是关于圈子论坛小程序的详细解析: 一、圈子论坛小程序的定义与功能 定义:圈子论坛小程序是一个集社交、分享、交流于一体的移动应用,用户…

微服务健康检查:如何通过Eureka实现服务自动剔除与恢复

微服务健康检查:如何通过Eureka实现服务自动剔除与恢复 引言 随着微服务架构的广泛应用,如何保证服务的高可用性和系统的稳定性成为了架构设计中的重要考量。服务注册与发现是微服务架构中的关键组件,它们确保了微服务能够被其他服务发现并…

FastAPI+React18开发通用后台管理系统用户功能实战

最近开发了一个React18的后台管理系统,登录界面如下: 如果登录成功了则提示并跳转到首页: 点击注销按钮则提示退出系统成功: 没有登录就访问首页则提示请先登录。 这些功能是怎么实现的呢? 先看看登录功能使用…

java:stream流

1、 stream是什么?有什么作用?结合了什么技术? 答:简化集合、数组操作的API,结合了lambda表达式。 2、说说stream流处理数据的步骤是什么? 先得到集合或者数组的stream流。 然后调用stream流的方法对数据进行处理。 获取处理…

进阶SpringBoot之 JDBC 篇

对于数据访问层,无论是SQL(关系型数据库)还是NOSQL(非关系型数据库), Spring Boot 底层都是采用 Spring Data 的方式进行统一处理 创建一个新项目,依赖勾选 JDBC API、MySQL Driver 项目创建好…

WPF—Frame控件和跳转详解

Frame控件和跳转 Frame控件 Frame是一个内容控件,提供导航到和显示内容的功能。 Frame可以托管在其他内容中,就像其他控件和元素一样。 常用属性 Source 设置该控件显示哪个资源,内容可以是任何类型的.NET Framework对象和 HTML 文件。 但…

android13 隐藏状态栏里面的飞行模式 隐藏蓝牙 隐藏网络

总纲 android13 rom 开发总纲说明 目录 1.前言 2.问题分析 3.代码分析 4.代码修改 5.编译运行 6.彩蛋 1.前言 android13 隐藏状态栏里面的飞行模式,或者其他功能,如网络,蓝牙等等功能,隐藏下图中的一些图标。 2.问题分析 这里如果直接找这个布局的话,需要跟的逻…

HTML 基本语法特性与 title 标签介绍

目录 title标签 HTML 的基本语法特性 对换行和缩进不敏感 空白折叠现象 标签要严格封闭 title标签 在 HTML 中&#xff0c;<title>标签起着至关重要的作用&#xff0c;它主要用于定义文档的标题。通常情况下&#xff0c;<title>标签被放置在<head>标签内…

java 使用ZooKeeper实现分布式锁

在Java中使用ZooKeeper&#xff08;简称ZK&#xff09;来实现分布式锁是一种常见的做法&#xff0c;因为ZooKeeper提供了一个分布式协调服务&#xff0c;其中包括了对分布式锁的支持。使用ZooKeeper实现分布式锁的主要思路是利用其临时顺序节点来管理锁的获取与释放。 以下是一…

华为HCIA考试大纲

数据通信与网络基础 ● 数据通信网络基础 ■ 数据通信基础概念 ■ 信息传递的过程 ■ 网络设备及基本功能 ■ 网络类型及拓扑类型 ■ 网络工程 ■ 网络工程师 ● 网络参考模型 ■ 数据及…

百度Q2财报:百度核心利润增长23%超预期 AI驱动业务高质量增长

北京时间8月22日&#xff0c;百度发布2024年Q2财报&#xff0c;显示季度总营收339亿元&#xff0c;百度核心营收267亿元&#xff0c;百度核心经营利润56亿元&#xff0c;同比增长23%&#xff0c;超出市场预期。 百度创始人、董事长兼首席执行官李彦宏表示&#xff0c;“随着时间…

Docker-harbor 私有仓库部署和管理

Docker-harbor 私有仓库部署和管理 概念 harbor&#xff1a;开源的企业级的docker仓库软件 仓库&#xff1a;私有仓库 公有仓库 harbor是有图形化的&#xff0c;页面ui展示的一个工具&#xff0c;操作起来很直观 harbor每个组件都是由容器构建的&#xff0c;所以安装harbo…

linux下的oracle启动命令

一、服务器断电后&#xff0c;手工启动oracle数据库步骤如下&#xff1a; 1、进入数据库服务器&#xff0c;切换到oracle用户,命令&#xff1a;su - oracle 2、启动数据库&#xff0c;命令&#xff1a; 1&#xff09; sqlplus / as sysdba 2) startup 3&#xff09;如果数据库已…