使用 longjmp/setjmp 进行 C 错误处理都有哪些“好”方法?

Posted

技术标签:

【中文标题】使用 longjmp/setjmp 进行 C 错误处理都有哪些“好”方法?【英文标题】:What are some "good" ways to use longjmp/setjmp for C error handling?使用 longjmp/setjmp 进行 C 错误处理有哪些“好”方法? 【发布时间】:2010-10-23 14:55:14 【问题描述】:

我必须在一个项目中使用 C,我正在考虑使用 longjmp/setjmp 进行错误处理,因为我认为在一个中心位置处理错误比返回代码要容易得多。如果有一些关于如何做到这一点的线索,我将不胜感激。

如果发生任何此类错误,我特别关心资源清理是否正确。

另外我该如何处理导致多线程程序使用它们的错误?

更好的是,是否已经存在一些用于错误/异常处理的 C 库?

【问题讨论】:

【参考方案1】:

如果您担心资源清理,您必须认真考虑 longjmp() 和 setjmp() 是否是个好主意。

如果您设计的资源分配系统实际上可以准确地清理,那么它是可以的 - 但这种设计往往很棘手,而且通常不完整,如果事实上,您的代码使用自己的标准库分配必须释放的资源。它需要特别小心,并且由于它不是完全可靠的,因此它不适合可能需要在多次使用 setjmp()/longjmp() 调用后存活的长时间运行的系统(它们会泄漏、扩展并最终导致问题)。

【讨论】:

【参考方案2】:

看看这个例子/教程:http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html

【讨论】:

使用该实现如何处理函数调用中的THROW? ex_buf__ 在使用 TRY-CATCH 块的函数中是本地的,没有其他函数看到该变量。由于线程安全,我还想避免使用全局变量。 @torbatamas thread_local 变量可能会对您有所帮助。【参考方案3】:

Symbian 以longjmp() 的形式实现了它的Leave 机制,这可以很好地了解您需要做的所有事情。

Symbian 有一个全局“清理堆栈”,如果发生跳转,您可以推送和弹出要清理的内容。这是 C++ 编译器在抛出 C++ 异常时执行的自动堆栈展开的手动替代方法。

Symbian 有“陷阱线束”,它会跳出来;这些可以嵌套。

(Symbian 最近根据 C++ 异常重新实现了它,但接口保持不变。

总的来说,我认为正确的 C++ 异常更不容易出现编码错误,并且比滚动您自己的 C 等效项要快得多。

(例如,现代 C++ 编译器非常擅长在不抛出“零开销”异常时;longjmp() 必须存储所有寄存器的状态等,即使以后不进行跳转,也可以基本上永远不会像异常一样快。)

使用 C++ 作为更好的 C,您只采用异常和 RAII,这将是一个很好的途径,应该使用 longjmp() 进行异常模拟对您很有吸引力。

【讨论】:

请使用正确的名称:longjmp / setjmp,而不是 'longjump'。 感谢您提供的信息。但是,问题是特定于使用 C 而不是 C++ @MeThinks,没关系,我的建议适用于 C;我概述了模拟堆栈展开(例如 Symbian 的清理堆栈)作为您在问题中提出的资源清理的方式。【参考方案4】:

我只发现一个用于setjmp()/longjmp(),它与错误处理无关。

确实没有必要为此使用它,因为它总是可以被重构为更容易理解的东西。 setjmp()/longjmp() 的使用与goto 非常相似,因为它很容易被滥用。通常,任何使您的代码可读性降低的事情都是一个坏主意。请注意,我并不是说它们天生就不好,只是说它们比替代品更容易导致糟糕的代码。

FWIW,他们非常宝贵的一个地方是我在行业早期所做的一个项目(MS-DOS 6 时间框架)。我设法使用 Turbo C 组合了一个协作多线程库,它使用 yield() 函数中的这些函数来切换任务。

我很确定从那时起我就没有碰过它们(或有必要)。

【讨论】:

+1 我也只使用过一次:在 DOS 游戏的复制保护中。我使用 longjmp 而不是普通的分支来混淆饼干。这有点奏效,因为生成的代码看起来不像一个分支。 :-) 有趣的巧合:我构建了一个协作线程 ca。 1985 年使用 setjmp()/longjmp() 在 SGI 工作站上以当时的 unix 风格运行。像魅力一样工作。由于它用于动画,它包括一个将所有线程同步到视频系统的垂直中断的集合点。然而,从那以后我没有直接使用它们。【参考方案5】:

到目前为止,异常是一种更好的通用机制,但在 C 过去的黑暗时期,我编写了一个包含命令 shell 的处理器模拟器。 shell 用于设置 jmp/longjmp 以进行中断处理(即处理器正在运行并且用户点击 break/ctrl-c,代码将 SIGINT 和 longjmps 捕获回 shell)。

【讨论】:

【参考方案6】:

我已经相当整洁地使用了setjmp/longjmp,以从回调中逃脱,而无需通过其他各种库级别进行协商。

这种情况(如果我没记错的话)是 yacc 生成的解析器中的代码可以检测到(非语法)问题,并希望放弃解析但在所有 yacc 生成的代码的另一面。另一个例子是在从 Expat 解析器调用的回调中。在每种情况下,都有其他方法可以做到这一点,但它们似乎比简单地以这种方式摆脱困境更加麻烦和晦涩。

正如其他答案所指出的那样,有必要小心清理,并且非常周到地确保 longjmp 代码只能在由 setjmp 动态保护的区域范围内调用.

在多线程编程的上下文中执行它?我敢肯定这不是不可能的,但是哦:现在就拿出你家装的阿司匹林吧。将setjmp/longjmp 对尽可能靠近可能是明智之举。只要匹配的setjmp/longjmp 对在同一个线程中,我希望你会没事,但是......要小心。

【讨论】:

以上是关于使用 longjmp/setjmp 进行 C 错误处理都有哪些“好”方法?的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 在c中使用goto进行错误处理

使用 C (Ubuntu) 进行套接字编程中的分段错误

使用C中的结构进行分段错误

我在使用C#进行编C/S程序的时候,设计器模式不能进入

使用结构对两个堆栈进行排序 - 编译错误

如何使用工具进行C/C++的内存泄漏检测