子程序由程序指令调用,以执行调用程序所需的函数。而中断是由诸如输入操作或硬件错误之类的事件发起的。但是处理器如何区分它们呢?
为了补充其他好的答案,我将更多地讨论函数调用与中断的逻辑相似性,以及它们之间的区别。
从逻辑角度来看,子例程或函数调用会挂起当前执行的调用程序,并将控制权转移到sub/func。当该sub/func完成时,它将控制权返回给调用者,并在调用后恢复。
这是从逻辑角度来看的——然而,就硬件而言,没有从调用者到被调用者的控制暂停、恢复或转移——只有机器代码指令的连续执行流,碰巧包括各种分支指令,如call
(jal
,例如在MIPS上)和ret
(jr
)
从调用者到被调用者的控制权转移发生在非常可控的情况下——这在很大程度上是通过软件协议、ABI和ABI中指定的调用约定实现的。根据软件约定,调用者和被调用者事先就参数的传递方式、返回值的传递方式以及被调用者可以自由调用的寄存器与返回时必须保留/恢复的寄存器达成一致。从调用者到被调用者的控制权转移发生在程序控制下的调用站点和返回站点——它们是作为执行程序指令的结果而同步发生的。
当中断发生时,它可能发生在任何两条指令之间——因此对于其中一条,没有"中断";呼叫约定";控制从用户代码(被中断的内容)到异常处理程序的传输。关于外部中断,异常处理程序不能完全理解被中断的代码(被中断的编码并没有试图对操作系统进行同步调用)。在这种情况下,必须假定所有处理器状态(如寄存器)在中断的代码中处于繁忙/正在使用状态,而中断的代码基本上是通过将控制权转移到异常处理程序而强制挂起的。
外部中断不会向被中断的代码返回值——它们会更改操作系统状态(设备状态),这可能会使其他一些进程从阻塞变为可运行。
由于外部中断可能发生在任何两条指令之间(与软件可以看到暂停/恢复点的调用不同),为了使线程可恢复,(与函数调用相比)需要保留更多的处理器状态,以防被中断的线程稍后读取该状态。
尽管现代硬件在任何时间点都有许多指令在运行,但它必须保持中断发生在两个(动态)相邻指令之间的错觉。在某种程度上,这是为了它可以指示中断程序中的程序计数器/指令指针,以及在哪里恢复它
由于外部中断,操作系统的状态会发生变化。由于这些变化,操作系统在从中断返回时,可以选择恢复与被中断线程不同的线程(甚至在另一个进程中),使被中断线程处于挂起状态以供以后恢复。以这种方式,可以进行线程的调度和交织,这与函数调用和函数返回调用者有很大不同(从逻辑上讲)。(一些用户级系统具有协同程序和光纤,它们表现出控制权的转移,而不是从呼叫者到被呼叫者和返回。)
总之,函数调用是在程序的控制下进行的,仅在受控点进行,并通过软件调用约定和某些用户模式调用和返回指令进行。然而,需要明确的是,调用和返回可以在没有这些专用调用和返回指令的情况下完成(它们可以通过替代指令序列来模拟,而不会有困难)。而外部中断是由外部设备向处理器发出信号触发的,可能发生在中断代码中的任何两条指令之间,部分原因是它们涉及特权更改,需要特权指令从中断中恢复&很难或不可能用其他常规指令模拟的悬架。
call
指令会调用与中断处理完全不同的行为。(例如x86https://wiki.osdev.org/Interrupts,以及当中断发生时,管道中的指令会发生什么?)。CPU切换到内核(supervisor)模式来处理中断,在具有单独权限级别的CPU上。
在那之后,CPU不在乎它是如何进入当前状态的。CPU只是执行指令;这取决于(操作系统或子程序的)程序员在那里放置有用的指令,例如在x86上以ret
(正常返回)或iret
(中断返回)结尾。
CCD_ 8不是"0";"特殊";,在x86上,它只是从堆栈中弹出一个返回地址到程序计数器中。你可以用其他(通常较慢的)方式做同样的事情。iret
也有定义明确的行为,而不是魔术,可能可以用popf
和远ref [rsp]
的正确组合或其他指令来模拟,也可能不依赖于GDT条目。不过,它仍然不是魔术,只是一个对体系结构状态进行特定更改的指令(例如,切换回用户模式,脱离监管模式)
其他ISAs的情况类似;不同的指令名称以及CPU保存足够状态以使内核能够在中断后返回用户空间的不同方式。
这因CPU而异:
在一些CPU上没有区别(除了中断是由一些硬件事件启动的):
call
指令和中断都将下一条指令的地址推送到堆栈,并进入函数或中断处理程序。ret
指令用于从函数或中断返回。
如果我还记得的话,20世纪70年代中期的i8080就是这样一个CPU的例子。
在其他CPU上,两者之间存在巨大差异:
在函数调用中,当输入中断时,只存储下一条指令的地址,而存储所有CPU寄存器。
因此,存在用于从函数返回(x86上的ret
)和用于从中断返回(x86中的iret
)的不同指令。这是必要的,因为离开中断与离开函数的工作方式不同。
MC6800(也是1970年代中期的芯片)就是这种CPU的一个例子。
大多数CPU(包括x86)都介于这两种变体之间。
甚至还有CPU需要从不同类型的中断返回不同的指令(一些PowerPC变体有四个不同的从中断返回的指令)!
还有Cortex-M,进入中断和进入函数的工作方式完全不同,但相同的指令可以用来离开中断和函数(尽管离开函数的工作原理与离开中断不同)。。。