所有现代CPU都有能力中断当前正在执行的机器指令。它们保存了足够的状态(通常,但并非总是在堆栈上),以便以后可以恢复执行,就好像什么都没发生一样(被中断的指令通常会从头开始重新启动)。然后他们开始执行一个中断处理程序,该中断处理程序只是更多的机器代码,但是放置在特殊的位置,因此CPU可以提前知道它在哪里。中断处理程序始终是操作系统内核的一部分:该组件以最大的特权运行,并负责监督所有其他组件的执行。1,2
中断可以是同步的,这意味着它们是由CPU本身作为对当前正在执行的指令所做的直接响应而触发的,也可以是异步的,这意味着它们是由于外部事件(如数据到达网络)而在不可预测的时间发生的。港口。有些人将术语“中断”保留为异步中断,而将同步中断称为“陷阱”,“故障”或“异常”,但是这些词都具有其他含义,因此我将坚持使用“同步中断”。
现在,大多数现代操作系统都有进程的概念。从最基本的意义上说,这是一种机制,计算机可以同时运行多个程序,但这也是操作系统配置内存保护的关键方面,这是大多数功能(但是,仍然不是全部)现代CPU。它与虚拟内存一起,可以更改内存地址和RAM中实际位置之间的映射。内存保护允许操作系统为每个进程提供其自己的私有RAM块,只有它可以访问。它还允许操作系统(代表某个进程)将RAM的区域指定为只读,可执行,在一组协作进程之间共享等。还将有一部分内存只能由内存访问。核心。3
只要每个进程仅以CPU配置为允许的方式访问内存,就看不到内存保护。当进程违反规则时,CPU将生成一个同步中断,要求内核进行处理。通常情况下,进程并没有真正违反规则,只有内核需要做一些工作才能允许进程继续。例如,如果需要将进程内存的页面“移出”交换文件,以释放RAM中的空间用于其他操作,则内核将标记该页面不可访问。下次该进程尝试使用该进程时,CPU将生成一个内存保护中断。内核将从交换中检索页面,将其放回原处,再次标记为可访问,然后恢复执行。
但是,假设该过程确实违反了规则。它试图访问从未映射过任何RAM的页面,或者试图执行被标记为不包含机器代码的页面,等等。通常称为“ Unix”的操作系统家族都使用信号来处理这种情况。4信号类似于中断,但它们由内核生成并由进程处理,而不是由硬件生成并由内核处理。流程可以定义信号处理程序用自己的代码,并告诉内核它们在哪里。然后,这些信号处理程序将执行,并在必要时中断正常的控制流程。信号都有一个数字和两个名称,其中一个是隐喻的缩写,另一个是隐喻性稍低的短语。当一个进程违反内存保护规则时,生成的信号为(按惯例)数字11,其名称为SIGSEGV
和“分段故障”。5,6
信号和中断之间的重要区别是每个信号都有默认行为。如果操作系统未能为所有中断定义处理程序,则这是OS中的错误,并且当CPU尝试调用缺少的处理程序时,整个计算机将崩溃。但是过程没有义务为所有信号定义信号处理程序。如果内核为进程生成信号,并且该信号保留其默认行为,则内核将继续执行默认操作,而不会打扰进程。大多数信号的默认行为是“不执行任何操作”或“终止此过程,并且可能还会产生核心转储”。SIGSEGV
是后者之一。
因此,总而言之,我们有一个违反了内存保护规则的过程。CPU暂停了该进程并生成了同步中断。内核发现该中断并SIGSEGV
为该过程生成了信号。让我们假设过程中并没有设立一个信号处理器SIGSEGV
,所以内核执行的默认行为,这是终止进程。这与_exit
系统调用具有相同的效果:关闭打开的文件,释放内存,等等。
到现在为止,还没有打印出任何人可以看到的消息,并且外壳(或更一般地说,刚终止的过程的父进程)根本没有涉及。SIGSEGV
进入违反规则的流程,而不是其父流程。但是,序列的下一步是通知父进程其子进程已终止。这可以用几种不同的方法,其中最简单的是当父已经等待这个通知,使用的一个发生wait
系统调用(wait
,waitpid
,wait4
等)。在这种情况下,内核只会导致系统调用返回,并向父进程提供一个称为退出状态的代码号。7退出状态通知父级为何子进程被终止;在这种情况下,它将得知子级由于SIGSEGV
信号的默认行为而被终止。
然后,父进程可以通过打印消息将事件报告给人类。shell程序几乎总是这样做。您的crsh
代码不包含执行此操作的代码,但是无论如何都会发生,因为C库例程在“幕后” system
运行了一个功能齐全的shell /bin/sh
。在这种情况下crsh
是祖父母;父进程通知的字段为/bin/sh
,它会打印其通常的消息。然后/bin/sh
,它自身退出,因为它无所事事,并且C库的实现system
接收到该退出通知。通过检查的返回值,您可以在代码中看到退出通知system
; 但它不会告诉您孙子进程是在段错误中死亡的,因为中间shell进程消耗了它。
脚注
某些操作系统没有将设备驱动程序作为内核的一部分来实现;然而,所有的中断处理程序还是要内核的一部分,所以这是否配置内存保护的代码,因为硬件不允许任何东西,但内核做这些事情。
可能存在一个称为“管理程序”或“虚拟机管理器”的程序,该程序比内核具有更高的特权,但是出于此答案的目的,它可以被视为硬件的一部分。
内核是一个程序,但不是一个进程。它更像一个图书馆。除了它们自己的代码之外,所有进程还不时执行部分内核代码。可能有许多只执行内核代码的“内核线程” ,但是在这里它们与我们无关。
当然,您可能不得不处理的又一个不能被认为是Unix实现的操作系统也是Windows。在这种情况下,它不使用信号。(事实上,它不具有信号;在Windows上的<signal.h>
界面完全由C库伪造。)它使用一种叫做“ 结构化异常处理 ”代替。
SIGBUS
而不是生成了一些违反内存保护的行为(“总线错误”)SIGSEGV
。两者之间的界限不明确,并且因系统而异。如果您编写了一个程序定义了一个处理程序SIGSEGV
,则最好为定义一个相同的处理程序SIGBUS
。
“分段错误”是运行原始Unix的其中一台计算机(可能是PDP-11)因违反内存保护而生成的中断的名称。“ 分段 ”是一个类型的内存保护的,但现在的术语“分段故障 ”一般是指任何类型的内存保护冲突。
可以通过其他所有方式通知父进程子进程已终止,直到父进程调用wait
并接收退出状态。只是其他事情首先发生。
crsh
这种实验是个好主意。感谢您让我们所有人都知道它及其背后的想法。