如何在Go代码中捕获C/C++库异常



我使用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程序崩溃,即使我写了恢复代码。(附言:我似乎无法恢复致命错误(。

我的场景:

  1. Go程序将接收来自MQ的消息
  2. Go程序调用C库来处理消息
  3. 标记消息处理完成

在此过程中,Go程序可能收到错误的消息(例如:无效的消息格式(。错误的消息可能会使C库崩溃,而且在Go程序中找不到它,当C库崩溃时,我什么都做不了,即使我想在Go程序重新启动时跳过错误的消息。

有什么方法可以从C/C++库中捕获异常吗?

或者总的来说,Cgo中错误处理的最佳实践是什么?

我想强调@Not_a_Golfer所说的:当操作系统遇到进程试图访问它从未尝试过的内存时,它会向进程发送SIGSEGV信号。

问题是,这种错误的原因可能确实是";无害的";(见下文(否则可能不会。

  • Harmless可能就像试图读取对进程无效的地址处的一些内存。最常见的情况是试图取消引用所谓的NULL指针。

    在这种情况下,进程可能不会覆盖一系列内存,如果你很幸运,中止操作可能会让进程缓慢前进。

    不过,这并不是独角兽和彩虹:如果进程在操作开始前分配了一些内存,那么很可能会出现内存泄漏。

  • 严重的情况是写入不用于进程的内存区域
    它们的问题是,当进程到达一个无效的内存区域时,它可能已经覆盖了自己的活动数据结构,而这不是预期的。

    在这种情况下,所有的赌注都会落空

无论导致无效内存访问的特定问题属于哪一类,请注意,这表明程序至少包含一个逻辑错误,并且执行该错误的代码路径已被执行。这意味着该过程现在处于某种未定义的状态,因为这样的错误很容易变成"错误";"传播":当程序中不相关的部分可能因为其逻辑所基于的不变量被无意中更改而开始行为不端时,它们可能会引起级联效应。

在您的情况下,代码似乎访问地址为0x90的内存,这看起来像是涉及NULL指针的经典指针算法(只是猜测,但仍然如此(。

在这种情况下,我会做的是:

  • 将这个库封装在一个单独的进程中,并通过任何类型的IPC与之通信
  • 一旦它死亡,产生另一个副本来代替它,然后重试

如果可能的话,请尽一切努力解决根本原因。


在操作系统捕获了对无效内存区域的访问后,正确恢复执行本身就很困难——例如,请参阅此
基本上,您必须实现一个自定义信号处理程序,它将以这样一种方式进行设置,即操作系统将重新开始执行进程的代码,而不是从实际访问该内存块并崩溃的CPU指令开始,而是从一个已知的好位置开始(据说应该是库入口点函数出口附近的某个地方,该函数在调用路径的某个位置执行了错误代码。
您需要正确恢复堆栈指针,可能是其他东西。

实际上,这不是你经常做的事情。
对库映像进行二进制补丁,以防止错误的代码路径被执行或将它们转移到已修复的对应程序,添加到映像中,这甚至可能会减少资源消耗——就像通过二进制补丁进行的错误修复一样,类似于TTD。

最新更新