我使用 Cgo 访问 Go 代码中的 C/C++ 库,我发现了一些异常日志,如下所示:
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x90 pc=0x7ff0fbdc23ff]
....
STACK ...
现在我可以确认异常来自 C/C++ 库,但是即使我编写了恢复代码,这个异常也会使我的 Go 程序崩溃。(PS:似乎我无法恢复 fatal error )。我的场景:
在这个过程中,Go 程序可能会收到错误的消息(例如:无效的消息格式)。错误消息可能会使 C 库崩溃,并且在 Go 程序中找不到,当 C 库崩溃时我无能为力,即使我想在 Go 程序重新启动时跳过错误消息。
有没有办法从 C/C++ 库中捕获异常?
或者一般来说,Cgo 中错误处理的最佳实践是什么?
最佳答案
我想强调@Not_a_Golfer 所说的:一个进程被发送到SIGSEGV
。当操作系统遇到它试图访问它一定从未尝试访问的内存时发出信号。
问题是这种错误的原因可能确实是“无害的”(见下文),也可能不是。
在这种情况下,该过程可能不会覆盖一定范围的内存,如果幸运的话,中止操作可能会导致该过程突然消失¹。
但是,这不是 unicorn 和彩虹:如果进程在操作开始之前分配了一些内存,那么您很可能最终会出现内存泄漏。
它们的问题在于,当进程遇到无效的内存区域时,它可能已经覆盖了自己的非预期实时数据结构。
在这种情况下,所有的赌注都是真的。
无论导致无效内存访问的特定问题属于哪个类,请注意它表明程序包含至少一个逻辑错误,并且执行了执行该错误的代码路径。这意味着该过程现在处于某种未定义的状态,因为此类错误很容易“传播”:当程序的其他不相关部分可能开始出现异常时,它们可能会导致级联效应,因为它们的逻辑所基于的不变量被无意更改。
在您的情况下,代码似乎访问地址
0x90
的内存这看起来像一个经典的指针算术涉及一个 NULL 指针(只是猜测,但仍然如此)。在这种情况下我会做的是:
无论如何,如果可能的话,请尝试解决根本原因。
¹ 在操作系统捕获对无效内存区域的访问后正确恢复执行本身就是一项艰巨的任务——请参阅 this例如。
基本上,您必须实现一个自定义信号处理程序,该处理程序将以这样的方式进行设置,即操作系统将重新开始执行您的进程代码,而不是从实际访问该内存块并爆炸但具有已知良好位置的 CPU 指令(这应该是库入口点函数的导出附近的某个地方,该函数在其调用路径的某个地方执行了错误的代码。
而且您需要正确恢复堆栈指针,并且可能是其他东西。
真的,这不是你经常做的事情。
对库镜像进行二进制修补以防止错误的代码路径被执行或将它们转移到它们的固定对应项,添加到镜像中,甚至可能会减少资源消耗——就像通过类似于 those done for TTD 的二进制修补完成的错误修复一样, 例如。
关于c++ - 如何在 Go 代码中捕获 C/C++ lib 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63185359/