调用 CFFI 函数时如何优雅地处理异常/崩溃?

How can I gracefully handle exceptions/crashes when calling a CFFI function?

提问人:sjohns 提问时间:10/19/2023 最后编辑:sjohns 更新时间:10/20/2023 访问量:51

问:

我正在使用 Python 的 CFFI 从 DLL 调用 C 函数。

from CFFI import FFI

my_ffi = FFI()
my_dll = my_ffi.dlopen(my_dll_path)
my_ffi.cdef('int my_func();')
my_c_int = my_dll.my_func()

有时,C 函数会崩溃、引发异常或这些异常的组合。这通常会导致调用 C 函数的 Python 脚本终止。

我希望能够做这样的事情:

try:
    my_c_int = my_dll.my_func()
except SomeKindsOfExceptionsIncludingTheForeignFunctionCrashing:
    handle_exception()

是否可以处理外部函数引发的异常以及外部函数中的崩溃?如果是这样,如何才能最好地完成?

编辑:具体来说,我想处理在外部函数中执行代码期间引发的异常以及它使用的库。我不想捕获和忽略硬件异常。我正在处理的一个典型异常是由套接字意外断开引起的。

我考虑过在单独的线程中调用 C 函数并监视该线程的超时。但是,C 函数实现了优化器的部分损失函数,因此我想避免每次调用 C 函数时启动新线程的开销。此外,一个新线程很混乱,感觉应该有一种更简单的方法来做到这一点。

我正在单独研究减少来自我的DLL的异常行为数量的方法,尽管这可能不会实现。

编辑:评论中建议了 SEH,但据我了解,这将发生在外部函数中,而不是在 Python 端。我的目标是能够使用 Python 来处理此类异常。这是对一个线程的响应,该线程似乎表明 ctypes 模块不支持 SEH:

如果解释器未在较低级别处理 OS 异常,或者 一个扩展模块,进程崩溃得很厉害。

但是,我正在使用 CFFI 模块,它的行为可能有所不同。

python-cffi

评论

0赞 Charles Duffy 10/19/2023
“C 函数会抛出异常”——这没有意义:C 根本没有异常。这实际上是C++(或回调到CPython API中的东西)吗?
0赞 sjohns 10/19/2023
好渔获。是的,C 函数调用用 C++ 编写的函数。
0赞 Charles Duffy 10/19/2023
没有一个好的通用答案——我什至认为使用线程是不够的,因为线程仍然可以损坏与线程共享的父进程的内存;人们真的希望整个进程边界 - 如果在运行可疑代码之前未在子项中关闭,则共享文件句柄和其他资源被视为可疑 - 在一般情况下是安全的。fork()
0赞 relent95 10/19/2023
这是不具体的。您必须区分由编程语言定义的异常和由操作系统定义的异常。你所说的“例外”是指后者。通常,当发生操作系统异常时,不应继续该过程,因为程序状态将未定义,继续程序可能会导致损坏或安全问题。如果您坚持,请参阅 SEH。如果你问它,做一个初步的研究,尝试实现一个原型,并用一个最少的可重复的例子和调试细节来问一个特定的问题。
1赞 sjohns 10/20/2023
@UlrichEckhardt感谢您的细节。我认为答案是,对于CFFI来说,这是不可能的。我会尝试使用解决方法。

答:

1赞 Armin Rigo 10/20/2023 #1

您可以通过围绕 C++ 代码编写自己的 C 包装器来间接使用 CFFI 调用 C++ 代码并捕获 C++ 异常。为此,您需要在 API 模式下使用 CFFI 并在部件中编写包装器;然后使用 CFFI 调用此 C 包装器。set_source()

您也可以直接使用 CPython C API,但它仍然需要相同的 C 包装器:这两种解决方案都对 C++ 异常没有任何理智,因此您必须捕获并转换它们。