为啥我的代码在 Windows 7 上不会出现段错误?

Posted

技术标签:

【中文标题】为啥我的代码在 Windows 7 上不会出现段错误?【英文标题】:Why won't my code segfault on Windows 7?为什么我的代码在 Windows 7 上不会出现段错误? 【发布时间】:2011-06-05 11:58:38 【问题描述】:

这是一个不寻常的问题,但这里是:

在我的代码中,我不小心在某处取消引用 NULL。但是,应用程序并没有因段错误而崩溃,而是似乎停止了当前函数的执行,并将控制权返回给 UI。这使调试变得困难,因为我通常希望收到崩溃警报,以便我可以附加调试器。

这可能是什么原因造成的?

具体来说,我的代码是一个 ODBC 驱动程序(即 DLL)。我的测试应用程序是 ODBC Test (odbct32w.exe),它允许我在我的 DLL 中显式调用 ODBC API 函数。当我调用具有已知段错误的函数之一时,ODBC 测试不会使应用程序崩溃,而是将控制权简单地返回给 UI,而不打印函数调用的结果。然后我可以再次调用驱动程序中的任何函数。

我确实知道,从技术上讲,应用程序调用 ODBC 驱动程序管理器,该管理器加载和调用我的驱动程序中的函数。但这无关紧要,因为我的段错误(或发生的任何事情)会导致驱动程序管理器函数也不返回(正如应用程序不打印结果所证明的那样)。

我的一位使用类似机器的同事遇到了同样的问题,而另一位则没有,但我们无法确定任何具体差异。

【问题讨论】:

我喜欢这个问题的标题......但我不能可能在没有错误的情况下发布它! 你似乎已经提供了一切,除了代码。也许编译器正在优化那些什么都不做的代码。 我想异常正在某个地方处理? 我和 EboMike 在一起,有东西会赶上 AV 取决于上下文 - 通常任何此类操作都会导致页面错误错误,因为您无法读取 0x00000000(或附近)。这个抛出将向上传播到任何处理程序接受它的堆栈。没有处理程序 -> 返回操作系统/运行时系统,一般来说,它会卸载有问题的 .exe 并终止进程。该链中有大量机会拦截该行为,不了解您的上下文,无法说出究竟是什么可能代表您进行调解。 【参考方案1】:

Windows 有non-portable language extensions (known as "SEH"),它允许您将页面错误和分段违规作为异常捕获。

操作系统库的某些部分(特别是在处理一些窗口消息的操作系统代码中,如果我没记错的话)有一个 __try 块,即使面对这样的灾难性错误,您的代码也会继续运行.您很可能在这些__try 块之一中被调用。悲伤但真实。

查看这篇博文,例如:The case of the disappearing OnLoad exception – user-mode callback exceptions in x64

更新:

我觉得在 cmets 中归因于我的想法有点奇怪。备案:

我确实没有声称 SEH 本身不好。我说它是“不可移植的”,这是真的。我还声称在用户模式代码中使用 SEH 忽略 STATUS_ACCESS_VIOLATION 是“可悲的”。我坚持这一点。我希望我有勇气在新代码中执行此操作,而您正在审查我的代码,您会冲我大喊大叫,就像我写了 catch (...) /* Ignore this! */ 一样。这是个坏主意。这对访问冲突尤其不利,因为获取 AV 通常意味着您的进程处于错误状态,您不应继续执行。

我确实没有争辩说 SEH 的存在意味着您必须吞下所有错误。当然,SEH 是一种通用机制,不能怪罪于每个白痴使用它。我所说的是,一些 Windows 二进制文件在调用函数指针时会吞下STATUS_ACCESS_VIOLATION,这是一个真实且可观察到的事实,而且这并不漂亮。请注意,他们可能有历史原因或情有可原的情况来证明这一点。因此“悲伤但真实”。

我确实没有在这里注入任何“Windows 与 Unix”的言论。一个坏主意在任何平台上都是一个坏主意。尝试在 Unix 类型的操作系统上从 SIGSEGV 恢复同样是粗略的。

【讨论】:

我不会将 SEH 称为语言扩展,我认为它更多的是操作系统提供的服务。我不太确定窗口消息回调 - 它们是什么?你是说 WNDPROC 吗?尽管 Windows 与 UNIX 不同,但仅凭这一点并不能使其逊色。 @Trevor - 我可能会建议在每台机器上的调试器中查看程序并检查堆栈,看看它是否真的发生了同样的事情。在 Windbg(“Windows 调试工具”,可从 MSFT 免费下载)中,您可能必须使用命令“sxe”,即使捕获到异常也会中断。 @asveikau SEH 通常被工具供应商用来实现 C++ 异常,或者实际上是其他具有异常的语言。 SEH 不会吞下异常,它会引发并传输它们。如果您调用某些 Win32 API 函数,并且 SEH 处于活动状态,并且您传入 NULL 指针,那么您可以在 Windows DLL 中引发 AV,我有时会在 kernel32 中看到这种情况(这实际上不是内核,只是为了混淆问题!)如果这些异常返回到我的应用程序并且仍未处理,那么,是的,我的应用程序将终止。 @asveikau 我的观点本质上是,在 Windows 上,使用 SEH,访问冲突导致异常是完全正常的,如果未处理,将终止应用程序。如果您选择在您的应用程序中静默吞下 SEH 异常,那么这是您的错误,而不是 Windows 中的设计缺陷。 @David Heffernan - 我会在 C 中调用 __try 扩展。C 没有异常,AFAIK __try 是在编译器中实现的,而不是通过宏魔法实现的,最后我知道 GCC 有不支持__try【参考方案2】:

取消引用 NULL 指针是一种未定义的行为,它几乎可以产生任何东西——seg.fault、给 IRS 的一封信或给 *** 的帖子:)

【讨论】:

这是按坏的升序排列吗? :) 这是 100% 正确的 - 但并不是这个特定问题的真正答案。 确实如此。 “未定义行为”意味着它可以在具有操作系统 X 的机器上崩溃,但不会在具有操作系统 Y 的另一台机器上崩溃。事实上,任何更改(不同的编译器、不同的平台、不同的配置、一天中的不同时间)都可能产生不同的结果。 @EboMike 或 Descending order of Madness【参考方案3】:

Windows 7 也有它的容错堆 (FTH),它有时会做这样的事情。在我的情况下,它也是一个 NULL 取消引用。如果你在 Windows 7 上开发,你真的想关掉它!

What is Windows 7's Fault Tolerant Heap?

http://msdn.microsoft.com/en-us/library/dd744764%28v=vs.85%29.aspx

【讨论】:

【参考方案4】:

了解不同类型的异常处理程序here——它们不会捕获相同类型的异常。

【讨论】:

【参考方案5】:

将您的调试器附加到所有可能调用您的 dll 的应用程序,打开该功能以在抛出异常时中断,而不仅仅是在 [debug]|[exceptions] 菜单中未处理。

ODBC 是大多数(如果不是全部)COM,因为此类未处理的异常会导致问题,这可能表现为奇怪地退出 ODBC 函数,或者像挂起并且永远不会返回一样糟糕。

【讨论】:

100% 同意 - 可能发生的是堆栈中更高的一些代码正在捕获异常。找到它的方法是在调试器下启动您的应用程序(或将调试器附加到您的应用程序)并启用捕获第一次机会异常。它应该很容易击中。

以上是关于为啥我的代码在 Windows 7 上不会出现段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的推文按钮不会出现在我的页面上?

如何在 Windows 上安装 OpenAI Universe 而不会出现错误代码 1?

为啥在 C 中使用错误的格式说明符会使我的程序在 Windows 7 上崩溃?

为啥我的 MySQL 数据库在 Windows IIS 6 上崩溃并出现 VMWARE 错误 1784

将 imageCollection 从 Google 地球引擎导入到 QGIS - 如果代码和 crs 似乎没问题,为啥地图不会出现在画布上?

我的酷狗为啥登陆不上呢?