异常控制流

本文深入解析了现代操作系统中的异常控制流(ECF),包括硬件层的中断、陷阱和故障,以及应用层的软件异常。讲解了异常的定义、处理器检测异常的分类,如中断、陷阱和终止,并探讨了C++和Java等语言的异常处理机制。理解这些概念有助于开发者高效应对系统状态变化和错误处理。

现代操作系统必须能够对系统状态的变化做出反应,这些系统状态不是被内部程序变量捕获的,而且也不一定要和程序的执行相关。比如,一个硬件定时器定期产生信号,这个事件必须得到处理。包到达网络适配器后,必须存放在内存中。程序向磁盘请求数据,然后休眠,直到被通知说数据已就绪。当子进程终止时,创造这些子进程的父进程必须得到通知。

现代系统通过使控制流发生突变来对这些情况做出反应。一般而言,我们把这些突变称为异常控制流(Exceptional Control Flow,ECF)。

异常控制流发生在计算机系统的各个层次。

  • 在硬件层,硬件检测到的事件会触发控制突然转移到异常处理程序。
  • 在操作系统层,内核通过上下文切换将控制从一个用户进程转移到另一个用户进程。
  • 在应用层,一个进程可以发送信号到另一个进程,而接收者会将控制突然转移到它的一个信号处理程序。—个程序可以通过回避通常的栈规则,并执行到其他函数中任意位置的非本地跳转来对错误做出反应。
    像 C++ 和 Java 这样的语言通过 try、catch 以及 throw 语句来提供软件异常机制。软件异常允许程序进行非本地跳转(即违反通常的调用/返回栈规则的跳转)来响应错误情况。非本地跳转是一种应用层 ECF,在 C 中是通过 setjmplongjmp 函数提供的。理解这些低级函数将帮助你理解高级软件异常如何得以实现。

异常

异常(exception)就是控制流中的突变,用来响应处理器状态中的某些变化。
在任何情况下,当处理器检测到有事件发生时,它就会通过一张叫做异常表(exception table) 的跳转表,进行一个间接过程调用(异常),到一个专门设计用来处理这类事件的操作系统子程序(异常处理程序(exception handler)) .当异常处理程序完成处理后,根据引起异常的事件的类型,会发生以下 3 种情况中的一种:

  • 处理程序将控制返回给当前指令 ,即当事件发生时正在执行的指令。
  • 处理程序将控制返回给下一条指令,如果没有发生异常将会执行的下一条指令。
  • 处理程序终止被中断的程序。

异常的分类

异常可以分为四类:中断(interrupt)陷阱(trap)故障(fault)终止(abort)。图 8-4 中的表对这些类别的属性做了小结。

类别原因异步/同步返回行为
中断来自 I/O 设备的信号异步总是返回到下一条指令
陷阱有意的异常同步总是返回到下一条指令
故障潜在可恢复的错误同步可能返回到当前指令
终止不可恢复的错误同步不会返回

软中断又叫做编程异常(programmed exception),是异常的一种。在计算机中,中断可以分为同步中断和异步中断两种。

  • 同步中断:同步中断也叫做异常。同步中断是当指令执行时由CPU控制单元产生,并且只有在该指令终止执行后CPU才会发出中断。
  • 异步中断:异步中断也叫做中断。异步中断是由其他硬件设备随机产生的。 异步中断又可以细分为处理器探测异常(process-detected exception)和编程异常(programmed exception)。具体细分如下:
    • 处理器探测异常:处理器探测异常是当CPU执行指令时探测到的一个反常条件所产生的异常。根据CPU控制单元产生异常时保存在内核态堆栈EIP寄存器的值,可以将处理器探测异常分为3类:
      • 故障(trap):故障通常可以纠正。发生故障时保存在EIP中的值是引起故障的指令地址。因此,当异常处理程序顺利完成,即故障纠正,就会重新执行引起故障的那条指令。缺页异常就是基于这个机制。
      • 陷阱(trap):陷阱指令执行后会立即报告给CPU。保存在内核态堆栈EIP的值是一个随后要执行的指令地址。因此,当陷阱处理程序终止时,就会接着执行下一条指令。只有当没有必要重新执行已终止的指令时,才触发陷阱。陷阱的主要用途是为了调试程序。
      • 异常终止(abort):异常中止出现在发生了一个严重的错误。此时控制单元出了问题,不能在内核态堆栈EIP寄存器中保存引起异常的指令所在的确切位置。因此,异常中止会使受影响的进程终止。
    • 编程异常:编程异常在编程者发出请求时发生。控制单元把编程异常作为陷阱来处理。因此,编程异常和陷阱类似,当编程异常处理程序终止时,紧接着执行下一条指令。

参考

  1. 软中断和硬中断
  2. Linux硬中断和软中断
<think>我们正在讨论程序控制指令的分类。用户询问是否可以分为正常控制流异常控制流。 根据计算机系统的一般概念: - 正常控制流(Normal Control Flow)是指程序按照指令顺序执行,或者通过跳转、调用、循环等指令改变执行流程,但这些改变是程序开发者有意设计的。 - 异常控制流(Exceptional Control Flow)是指由于某些异常事件(如硬件中断、系统调用、程序错误等)导致的非预期控制流转移。 在指令集架构层面,程序控制指令通常包括: 1. 跳转指令(JMP) 2. 条件分支指令(Jcc) 3. 子程序调用和返回(CALL/RET) 4. 循环控制指令(LOOP等) 这些都属于正常控制流指令,因为它们是由程序显式安排的流程改变。 而异常控制流通常由以下情况触发: - 硬件中断(如时钟中断、I/O中断) - 处理器异常(如除零异常、缺页异常) - 系统调用(通过软中断或专用指令,如x86的INT/SYSCALL) 在高级语言中,异常处理(如try-catch)也是异常控制流的一种表现,但在机器指令层面,它通常通过跳转指令和特定的异常处理机制实现。 因此,我们可以将程序控制指令分为两类: 1. 正常控制流指令:包括跳转、分支、调用、返回、循环等。 2. 异常控制流指令:包括中断指令(如INT)、系统调用指令(如SYSCALL/SYSENTER)以及处理器内部用于异常处理的机制(这些通常不是显式的指令,而是由硬件触发)。 注意:异常控制流指令通常不是由程序直接调用(除了系统调用),而是由硬件或操作系统在特定条件下触发。 参考用户提供的引用: 引用[1]和[2]讨论了Java中的异常处理,将异常分为受检异常和非受检异常(RuntimeException),这属于高级语言层面的异常控制流。 引用[3]和[4]则提到了异常捕获和处理,也属于高级语言层面。 在指令集层面,异常控制流通常通过中断和异常处理机制实现,而程序控制指令主要处理正常控制流。 因此,回答用户的问题:是的,程序控制指令可以分为正常控制流指令和异常控制流指令。 下面我们详细说明: ### 一、正常控制流指令 这类指令是程序主动改变控制流的指令,包括: 1. 跳转指令(JMP) 2. 条件分支指令(JE, JNE, JG等) 3. 子程序调用和返回(CALL/RET) 4. 循环指令(LOOP, LOOPZ等) 这些指令的特点是由程序显式使用,控制流转移的目标地址在指令中指定或由寄存器给出。 ### 二、异常控制流指令 这类指令用于处理非预期的或由外部事件触发的控制流转移,包括: 1. 中断指令(如x86的INT n):主动触发软件中断,通常用于系统调用。 2. 系统调用指令(如x86的SYSCALL/SYSENTER):专门用于快速系统调用。 3. 中断返回指令(如IRET):用于从中断处理程序返回。 4. 处理器异常:由硬件自动触发,例如除零异常(对应x86的DIV指令出错时)或页错误,这些不是指令,而是事件。 注意:异常控制流指令的执行通常伴随着处理器状态的保存和恢复(如压栈保存现场),以及控制权转移到操作系统内核。 在高级语言中,异常处理机制(如try-catch)在底层通常通过正常控制流指令(跳转)和操作系统/运行时的异常处理机制(异常控制流)共同实现。 因此,我们可以这样总结: 程序控制指令在指令集层面可以分为: - **正常控制流指令**:程序主动控制的跳转、分支、调用、返回、循环等。 - **异常控制流指令**:用于响应外部事件或错误的指令(如INT、SYSCALL、IRET),以及由硬件自动触发的异常处理机制。 需要说明的是,硬件自动触发的异常处理机制并不是一条指令,而是一个过程,但它的效果相当于控制流的改变。 下面我们根据用户提供的引用,看看高级语言层面的异常控制流与指令集层面的关系: 引用[1]中展示了Java的FileInputStream类,其中read方法抛出了IOException(受检异常)。在底层,当发生I/O错误时,操作系统会产生一个中断或异常信号,然后由JVM捕获并转换为Java异常抛出。 引用[2]提到,受检异常用于期望调用者能够恢复的情况,而非受检异常(RuntimeException)通常表示程序错误。在底层,非受检异常可能由硬件异常(如空指针访问)触发,而受检异常则可能由系统调用错误(如文件不存在)触发。 引用[3]和[4]则说明了异常捕获的语法和常见异常类型,这些异常在底层都对应着异常控制流的处理。 因此,从整个计算机系统的角度,控制流可以分为正常和异常,而指令集也提供了相应的指令支持。 最后,我们生成相关问题:</think>### 程序控制指令的分类:正常控制流异常控制流 程序控制指令可分为**正常控制流**和**异常控制流**两类,二者在触发机制、处理逻辑和设计目标上存在本质区别。以下是具体分析: --- #### 一、正常控制流指令 通过**显式指令**主动改变程序执行顺序,是开发者预期的流程控制。 1. **核心类型**: - 跳转指令(`JMP`/`Jcc`) - 子程序调用/返回(`CALL`/`RET`) - 循环控制(`LOOP`) 2. **特点**: - **主动触发**:由程序显式调用(如`CALL SUB_ROUTINE`)。 - **可预测性**:流程转移目标明确(如跳转至标号`LABEL`)。 - **堆栈管理**:依赖堆栈保存返回地址(`CALL`压栈,`RET`弹栈)[^1]。 --- #### 二、异常控制流指令 由**非预期事件**触发的控制流转移,用于处理错误或外部中断。 1. **触发条件**: - **硬件异常**:除零错误、缺页中断等(如`#DE`除零异常)。 - **软件异常**:显式抛出异常(如`throw new IOException()`)[^1]。 - **外部中断**:时钟中断、I/O设备信号等。 2. **处理机制**: - **中断向量表**:通过预定义入口跳转至处理程序。 - **上下文保存**:自动保存寄存器状态(如`EFLAGS`、`CS:IP`)。 - **异常分类**: - **受检异常**(Checked):需显式处理(如Java的`IOException`)[^2]。 - **非受检异常**(Unchecked):逻辑错误(如`NullPointerException`)[^4]。 3. **示例**: ```java // Java异常控制流(受检异常) public int read() throws IOException { return read0(); // 可能触发IOException[^1] } ``` --- ### 核心区别 | **特性** | 正常控制流 | 异常控制流 | |------------------|---------------------------|---------------------------| | **触发方式** | 显式指令(如`CALL`) | 事件驱动(错误/中断) | | **目标地址** | 固定标号或地址 | 通过中断向量表动态跳转 | | **恢复机制** | 通过`RET`返回原流程 | 需显式恢复上下文或终止程序 | | **设计目标** | 实现分支、循环、模块化 | 容错处理或响应外部事件 | > **关键洞察**: > - 正常控制流是**程序主动设计**的流程(如函数调用);异常控制流是**被动响应**机制(如错误处理)[^2][^4]。 > - 异常控制流通常需要操作系统/运行时环境支持(如中断处理程序)[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值