为什么当我解引用空指针时操作系统不会崩溃?

3

解引用空指针会导致未定义的行为。实际上,这通常意味着我的程序将崩溃。但是为什么操作系统不会崩溃呢?因为如果我的程序解引用空指针并由操作系统运行,根据逻辑传递规则,这意味着操作系统尝试解引用空指针。为什么操作系统不进入“未定义行为”状态呢?


2
从C++标准的角度来看,您的操作系统可能会崩溃。 - Flexo
2
实际上这意味着我的程序可能会崩溃是错误的。实际上,这意味着任何事情可能发生。 - Xeo
2
因为编写操作系统的人知道他们在做什么? - Wooble
@Xeo稍微改了一下。顺便说一句,从C++的角度来看,任何事情都可能发生,但从操作系统的角度来看,处理这种情况可能有明确定义的规则。(至少我是这么期望的。) - StackedCrooked
3
大多数操作系统通常会对非特权进程干扰其他进程的能力做出更强的保证,这有助于提高系统的稳定性和安全性。 - Flexo
显示剩余2条评论
9个回答

11

C++标准没有定义行为,无论是保证崩溃还是执行其他操作。这并不妨碍操作系统定义行为-它不是C++程序,因此不必遵守C++程序的“规则”[1]。即便如此,操作系统也不会自己取消引用指针。

在大多数现代平台上,访问取消引用指针的目标将导致内存管理硬件引发异常(通常称为“分段错误”或“保护错误”)。内核捕获该异常,可以确定是哪个进程造成的,然后要么终止该进程,要么向其发送信号。

因此,在此类平台上,取消引用空指针的进程的默认行为将是崩溃;操作系统本身没有任何理由崩溃。

[1] 我所说的是程序应该是良好形式的,避免未定义行为的非正式“规则”,不要与由语言标准指定的C++实现的正式“规则”混淆。


1
请注意,在C++中,解除引用指针本身不会引发段错误,因为您只是从中获取了一个引用。但是,通过空指针(或引用)访问任何内容都会引发段错误。 - Xeo
@Xeo:确实,我有点随意地使用了“解引用”这个词。 - Mike Seymour
“这不是一个C++程序,因此它不必遵守C++程序的规则”这个说法有点不合理。C++并不要求“未定义行为”,它只是断言它不定义行为。其他人很可能会定义。操作系统所使用的语言是无关紧要的(如果不是C++,那么很可能是C,这与你的观点完全相反)。你应该完全删除这句话,这篇文章没有它也能让人理解。” - user541686
@Mehrdad:我指的是C++程序的非正式“规则”(即它们应该是良好形式并避免未定义行为),而不是C++实现的正式“规则”(即它们应该实现语言定义的行为)。值得注意的是,操作系统不必用定义明确的C++编写,因此可以通过定义C++语言不支持的行为来处理(例如)无效指针。如果我没有包含足够完整的单词定义,则表示抱歉。 - Mike Seymour

3

每个主要的操作系统都会保护内存访问。你不能简单地编写一个程序来操作未为其分配的内存(假设指针未初始化,那么它可以是任何地址)。因此,每当一个程序尝试访问不属于它的某个地址空间时,操作系统将发送一个信号来终止该程序(导致最终著名的“分段错误”,这对任何C/C++程序员都很熟悉)。


在我写这篇文章的时候,这个回答已经得到了一个赞。StackOverflow应该会自动更新答案。 - Sebastian

2
因为操作系统必须要做些什么,崩溃会给用户带来非常不好的体验。
操作系统并不是为了在C标准的抽象机器上运行而编写的。它是针对实际硬件编写的,这些硬件以真实的方式对标准所称的“未定义”情况做出反应,因此它(确实必须)考虑到这些真实的行为。如果没有考虑到这些行为,操作系统将在不同的硬件上表现不同,这有点违背了拥有操作系统的目的,不是吗?
在你说“未定义行为是未定义的,让糟糕代码的用户自己搞砸吧”之前,请想象一下单个意外的缓冲区溢出能够导致整个服务器发生段错误的安全问题。

2

首先,UB代表“任何事情都可能发生”。然而,在实践中,现代操作系统提供了内存保护 - 当程序试图解引用空指针时,该尝试会在CPU内部触发一个中断,由操作系统捕获和处理,然后操作系统停止该程序,接着继续运行,就好像没有发生任何不良事件。


我能在我的C++程序中处理这个中断吗? - StackedCrooked
据我所知,唯一无法捕获的信号是SIGKILL/SIGSTOP(在阅读signal.h后进行了编辑)。您可以随时查看:man signal.h - Sebastian
@StackedCrooked:这将取决于实现。在Visual C++中,这些情况可以被捕获,但无法合理地处理。 - sharptooth
@StackedCrooked,Unix 把中断转化为信号(SIGSEGV),现在有意义地处理它可能会很复杂。 - AProgrammer

1

在 UB 方面,逻辑传递没有规则。你的假设是错误的。

UB 的意思是任何事情都可能发生,所以在编写不良的操作系统时,你的程序可能会导致操作系统崩溃。不要排除这种可能性。

此外,你的程序并不是因为解引用 NULL 指针而崩溃,而是因为操作系统告诉它崩溃了。


你的第二个陈述似乎与你的第一个相矛盾。 - StackedCrooked
@StackedCrooked 我不明白是怎么回事。 - Luchian Grigore
你说一个程序的UB特性会影响操作系统。 - StackedCrooked
@StackedCrooked 是的,但不是因为它在任何情况下都可以传递给操作系统。这是因为C++程序中的UB可能导致任何事情发生。好吧,现在我写下来了,我明白你的意思了。但这不是我想说的。 - Luchian Grigore

1

操作系统设置了一个故障处理程序,如果内存访问违反了操作系统强制执行的规则(例如访问空地址),则会调用该程序。如果您的程序即将对空指针进行解引用操作,则会调用此故障处理程序,并在访问被禁止的内存区域之前结束程序。因此,您的程序实际上从未对空指针进行解引用操作,而是在尝试时被捕获。

检测禁止内存访问的机制通常使用硬件支持,如页表或内存分段。

如果操作系统内核本身对空指针进行解引用操作,则通常在尝试这样做时停止运行。您将看到蓝屏、内核 oops 或类似的情况。如果继续运行,可能会导致“未定义行为”。

请注意,“未定义行为”一词仅在 C 或类似语言中有确切的定义,处理器并不真正关心 - 通常,在没有足够权限访问内存区域时发生的情况在架构上下文中非常明确定义。


硬件会检测到这一情况,并向操作系统发送信号(即调用操作系统注册的处理程序)。然后,操作系统通过终止该程序来做出反应。 - Ernest Friedman-Hill
@ErnestFriedman-Hill 硬件负责执行所有操作,包括运行操作系统、程序,并处理对内存地址0的访问。你的观点是什么? - Gunther Piez
你说:“操作系统检测到你的程序即将取消引用空指针并结束它...”。这里还有其他几个答案,正确指出专用内存管理硬件检测访问并通知操作系统,然后采取行动。操作系统并不会查看你的程序执行的每一条指令。 - Ernest Friedman-Hill
好的,我知道那个表述不够准确。实际上发生的是操作系统跳转到错误处理程序,你肯定知道的。所以说“通知”也并不是很准确 :-) “专用内存管理硬件”(你说的是页表吗?)已经由操作系统设置好,通常被认为是操作系统的一部分结构,尽管它们直接被硬件读取。 - Gunther Piez

1

因为大多数程序运行在用户模式下,而操作系统运行在内核模式下。内核模式接近物理硬件(他们说“接近金属”)。内核模式程序(操作系统、一些服务、驱动程序等)在CPU的第0环中运行。用户模式程序在更高的环上运行。在CPU的第N环上运行的用户模式程序无法访问任何低于N的程序或内存。如果尝试这样做,将不被允许!

所有程序都获得它们的逻辑地址,并由操作系统分配。当程序尝试读取或写入某些内存时,操作系统会进行逻辑到物理寻址。如果程序尝试访问没有权限的地址,则操作系统将抛出异常。此异常可以由程序本身(同一线程中的本地异常处理程序)处理。如果没有,就会附加全局异常处理程序。如果本地EH无法处理,调试器也可能会介入。这取决于操作系统如何/何时将异常路由到调试器和/或全局异常处理程序。这还取决于异常类型(例如空指针访问),如果操作系统允许本地/全局/调试器处理它或不允许。如果没有人处理它,操作系统将终止进程(并可能创建崩溃转储、分段故障核心转储)。

如果进程没有被调试(只适用于Windows)并且安装了某些调试器,则操作系统可能允许用户调试它。

如果内核模式程序有什么不好的行为,它将会使操作系统崩溃。我不是Linux专家,所以不知道Linux的行为。但是在Windows的情况下,蓝屏死机会让你的显示器发出蓝色的亮光!


0
因为如果我的程序取消引用空指针,而我的程序由操作系统运行,那么根据逻辑传递的规则,这意味着操作系统尝试取消引用空指针。为什么操作系统不进入“未定义行为”状态?
这是错误的。有一种叫做内存保护的东西,这就是为什么你的程序被终止的原因。是操作系统在保护自己(在内存使用方面)。

0

抱歉,什么是“逻辑传递性”的规则?操作系统的设计之一就是保护程序免受其他程序的不当行为影响。特别是,操作系统不应因为你的程序尝试做一些愚蠢的事情而崩溃。

在没有内存保护的操作系统中,通过空指针(或任何无效指针)访问确实可能导致操作系统崩溃(如果操作系统恰好将位置0用于某个重要的内容)。

但这与逻辑传递性无关。这与你的程序访问属于另一个程序的内存有关。在这种情况下,任何一个程序都可能崩溃。


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