中断和异常

23

我在这里看到了关于异常的几个问题,其中一些暗示中断可以作为异常,但没有一个清晰地解释这两者之间的联系。

  • 什么是中断?

  • 什么是异常?(请解释你所知道的每种语言的异常,因为它们之间存在一些差异)

  • 何时异常会成为中断,反之亦然?

11个回答

22

您的处理器将具有多个外部中断引脚。通常这些引脚与硬件连接,并用于指示某些外部事件发生。例如,如果您正在使用串行端口,则UART将引发连接到处理器上其中一个中断引脚的引脚,以指示已接收到一个字节。

其他外设,如定时器、USB控制器等,也会根据某些外部事件生成中断。

当处理器在其外部中断引脚之一上接收到信号时,它将立即跳转到内存中的某个指定位置并开始执行。执行的代码通常称为ISR,或者中断服务例程。除非您正在实现驱动程序或执行某种嵌入式软件,否则您很少会遇到ISR。

不幸的是关于异常问题的答案不太清晰-此页面中的其他答案列出了3种不同的含义。

Ron Savage的答案涉及软件结构。这纯粹是一个应用程序级别的异常,在这里一段代码能够指示另一段代码可以检测到的错误。这里根本没有硬件参与。

然后,还有任务看到的异常。这是操作系统级别的结构,用于在任务执行非法操作时终止任务,例如除以0、非法访问内存等。

第三个是硬件异常。在行为方面,它与中断相同,即处理器将立即跳转到某个指定的内存位置并开始执行。异常与中断的区别在于,异常是由处理器检测到的一些非法活动引起的。例如,处理器上的MMU将检测到非法的内存访问并引发异常。这些硬件异常是操作系统执行其清理任务的初始触发器(如上面的段落所述)。


这个异常类别解答了我的困惑,即C标准没有异常而C++有异常。例如,在C中,除以零会导致未定义的行为,而不是抛出异常。它可能有一些处理硬件ISA异常的方式。而在C++中,硬件异常被不同地处理,并最终作为软件异常对象传播。 - Shichu Zhu

11

中断是由CPU外部设备(定时器滴答声、磁盘操作完成、网络数据包到达等)生成的,与程序执行异步。异常与程序执行同步(例如除以零,访问无效地址)。

除非你的程序在没有操作系统的情况下执行(或者你正在开发操作系统),否则它永远不会看到原始异常/中断。它们被操作系统捕获并处理(中断),或被转换为其他形式后再反射回用户程序(例如UNIX上的信号,Windows上的结构化异常处理(SEH)),在这里它有机会处理它。


9

中断表示处理器核外部的某些东西需要它的注意。它打断了程序的正常流程,执行了一个中断服务例程(ISR),通常会返回到中断发生之前的位置。

基本主题有很多变化:中断可能由软件生成,ISR之后另一个任务可能获得CPU等等。关键点在于,中断可以在任何时候发生,原因是代码/CPU无法控制的。

异常的定义有点棘手,因为它可能有三个层次的含义:

硬件异常

某些处理器(例如PowerPC)定义了异常来指示发生了某种异常情况:系统复位、无效地址、某些虚拟地址转换缓存未命中等等。

这些异常也用于实现断点和系统调用。在这种情况下,它们几乎像中断一样工作。

操作系统异常

其中一些硬件异常将由操作系统处理。例如,您的程序访问无效内存。这将导致硬件异常。操作系统具有该异常的处理程序,很可能操作系统会向您的应用程序发送一个信号(例如SIGSEGV),表示存在问题。

如果您的程序安装了信号处理程序,则信号处理程序将运行并希望处理该情况。如果您没有信号处理程序,则可能会终止或暂停程序。

我认为Windows的结构化异常处理程序(SEH)属于这种类型的异常。

软件异常

一些语言(如Java、C++和C#)具有软件异常的概念,其中语言提供了与程序操作相关的不可预见或异常条件的处理。在这种情况下,在代码的某个点引发异常,并且程序执行堆栈上方的某些代码会“捕获”异常并执行。这就是try/catch块所做的事情。


8

中断是由硬件或特定的CPU指令生成的CPU信号。这些信号会导致中断处理程序被执行。例如,来自I/O硬件的I/O信号会生成中断。

异常可视为中断的软件版本,仅影响其进程。

我不确定具体细节,但异常可能通过中断来实现。


5

我将详细介绍中断是什么,因为还有一种关键类型的中断没有被处理:定时器。

但首先,让我退后一步。当您收到一个中断时,您的中断处理程序(位于内核空间)运行,通常禁用中断,处理任何待处理的业务(处理刚到达网络上的数据包,处理按键等),然后(记住此时我们仍在内核中)确定下一个应该运行的进程是什么(可能是同一个进程,也可能是不同的进程,这取决于调度程序),然后运行它。

在任何给定时间,处理器上只有一个进程在运行。当您使用多任务操作系统时,它们之间的切换方式称为上下文切换 - 基本上是将处理器的寄存器转储到内存中,流传递到新进程,当进程完成时,您会切换到其他东西。

所以,假设我编写了一个简单的C程序来计算所有数字、斐波那契数列或其他无休止循环的内容。甚至更好的是:在while(1)循环内旋转。其他系统进程如何获得运行机会?如果没有发生任何事情来引起中断怎么办?

答案是您有一个定时器设备不断地发出中断。这就是防止旋转进程导致整个系统崩溃的方法。尽管我要注意的是,中断处理程序会禁用中断,因此如果您执行一些无限制地阻塞的操作,您可能会使整个系统崩溃。


5

异常

异常是指处理器执行不在其正常路径上的代码。这是对正常操作的“例外”,正常操作通常是通过代码和控制结构进行线性移动。不同的编程语言支持各种类型的异常,通常用于处理程序运行期间的错误。

中断

中断通常是硬件级别的异常。中断是处理器中的物理信号,告诉CPU存储当前状态并跳转到中断(或异常)处理程序代码。一旦处理程序完成,原始状态将被恢复,处理过程可以继续。

即使是有意的,中断也总是一个异常。中断可能表示:

  • 错误,例如内存访问违规
  • 操作系统需要执行操作以支持正在运行的程序,例如软件中断或内存分页请求
  • 硬件设备需要注意,例如接收到的网络数据包或空传输缓冲区

这些都会强制处理器暂停其当前活动以处理引发的异常,只有在中断处理程序完成后才能继续。

陷阱

在中断方面,常见的陷阱是竞态条件。例如,您可能有一个定期增加全局实时时钟的中断。时钟可能是32位机器上的64位。

如果程序正在读取时钟并获取第一个32位单词,然后发生中断,则一旦中断处理程序退出,进程将获取第二个32位单词,并且数据将不一致-两个单词可能不同步。如果您尝试使用互斥锁或信号量来锁定进程中的变量,则中断将挂起等待锁定并停止系统(死锁),除非处理程序和使用数据的进程都非常小心地编写。编写中断程序时很容易遇到麻烦。

可重入函数也是另一个问题。如果在程序代码中执行funcA,在其中断中也执行funcA,则由于共享变量(静态变量、堆变量、类等),可能会产生意外后果。通常希望在中断处理程序中尽可能少地执行代码,并经常设置标志,以便进程稍后可以完成实际工作,而无需担心冲突。

在某些方面,这类似于为多处理器开发,也是为什么内核编程仍被许多人认为是黑魔法的原因之一。

-Adam


当你给我点踩的时候,能否请你在评论中解释一下我的回答有什么问题?如果这么多人不同意我的回答,那显然我还有很多需要学习的地方。 - Adam Davis

4

中断通常会定期发生(尽管有时它们是不规则的)... 它们打断了CPU,因为某些重要事件刚刚发生,需要立即处理。

异常应该是规则之外的例外情况; 这些由软件引发,因为发生了意外情况,这是您尝试解决问题的机会,或者至少是优雅崩溃的机会。


2
当你谈论中断和异常时,通常是在接近硬件级别的代码上,中断和异常往往是由硬件和软件部分实现的。
中断是与处理程序向量相关联的硬件事件(或在汇编语言中手动触发),可用于处理中断事件,例如IO完成、IO错误(磁盘内存故障)、IO事件(例如鼠标移动)。当出现意外中断时,中断可能引起异常。
异常是一种意外行为,通常在使用硬件时出现,这些异常来自中断,并使用中断处理程序在软件中单独处理。我们几乎总是将编程语言伪装成某种控制结构。

2
一般来说,中断是一种硬件实现的陷阱。你为特定的中断(如除零、外设数据可用、计时器到期)注册一个处理程序,当该事件发生时,整个处理系统将停止运行,你快速处理中断,然后继续执行任务。这些通常在设备驱动程序或内核中实现。
异常是一种软件实现的处理代码错误的方式。你设置特定(或通用)异常的处理程序。当异常发生时,语言运行时会开始展开堆栈,直到找到该特定异常的处理程序。此时,你可以处理异常并继续执行,或退出程序。

2

保持简单...

当你完成处理中断后,通常会返回到被中断前的工作状态。

处理异常包括丢弃当前正在进行的层次,直到冒泡到可以处理(捕获)异常的地方。

在处理中断时,您可能会决定抛出异常,但这并不意味着您必须将中断本身视为异常。异常不会“中断”(因为这将意味着可能回到中断之前的工作状态);相反,它们会“中止”(某个子集)您当前的活动。

正如已经多次提到的那样,中断通常由外部实体(例如硬件或用户,如鼠标单击或按下CTRL-C键)触发,而异常是由软件同步检测到“问题”或“异常情况”而生成(抛出)的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接