如何防止python在退出时释放ctypes回调?

如何防止python在退出时释放ctypes回调?,python,callback,ctypes,atexit,Python,Callback,Ctypes,Atexit,假设我有以下共享库要由ctypes加载。它允许您注册一个回调,以便在程序退出或您自己调用它时调用: #include <stdlib.h> static void (*callback)(void); void invoke_callback(void) { callback(); } void set_callback(void (*new_callback)(void)) { callback = new_callback; } void init(void

假设我有以下共享库要由
ctypes
加载。它允许您注册一个回调,以便在程序退出或您自己调用它时调用:

#include <stdlib.h>
static void (*callback)(void);

void invoke_callback(void)
{
    callback();
}

void set_callback(void (*new_callback)(void))
{
    callback = new_callback;
}

void init(void)
{
    atexit(invoke_callback);
}
我希望这个的输出如下所示:

callback invoked
callback invoked
不幸的是,它看起来更像这样:

callback invoked
Segmentation fault
你会问,这是为什么?那么,在调用
atexit
函数时,python解释器似乎已经取消了先前包含回调的内存分配:

(gdb) backtrace
#0  0x000000000049b11d in ?? () <- uh-oh
#1  0x000000000046d245 in ?? () <- ctypes' wrapper?
#2  0x00007ffff6b554a9 in ?? () <- ctypes
   from /usr/lib/python2.7/lib-dynload/_ctypes.x86_64-linux-gnu.so
#3  0x00007ffff6944baf in ffi_closure_unix64_inner ()
   from /usr/lib/x86_64-linux-gnu/libffi.so.6
#4  0x00007ffff6944f28 in ffi_closure_unix64 ()
   from /usr/lib/x86_64-linux-gnu/libffi.so.6
#5  0x00007ffff673e71d in invoke_callback () at test.c:6 <- me
#6  0x00007ffff6f2abc9 in __run_exit_handlers (status=0, 
    listp=0x7ffff72965a8 <__exit_funcs>, 
    run_list_atexit=run_list_atexit@entry=true) at exit.c:82
#7  0x00007ffff6f2ac15 in __GI_exit (status=<optimized out>) at exit.c:104
#8  0x00007ffff6f14b4c in __libc_start_main (main=0x497d80 <main>, argc=2, 
    argv=0x7fffffffe408, init=<optimized out>, fini=<optimized out>, 
    rtld_fini=<optimized out>, stack_end=0x7fffffffe3f8) at libc-start.c:321
#9  0x0000000000497ca0 in _start ()
(gdb)回溯

#0 0x000000000049b11d英寸??()我可能会迟到,但我最近遇到了类似的问题。基本上,回调是由Python进行垃圾收集的。如果您这样做:

callback_type = ctypes.CFUNCTYPE(None)
wrapped_callback = callback_type(callback)
shared.set_callback(wrapped_callback)

它应该可以解决segfault。

您可以使用Python的
atexit
模块而不是在C库中注册回调吗?如果没有,则需要为回调编译一个小型库。Python解释器在调用C
atexit
函数时早已被拆毁。遗憾的是,没有拆毁——回调是库的该部分(一个事件循环)中相当重要的一部分。我正在实习,所以我会问我的主管,当周一发生这种情况(很少)时,他们想做什么。谢谢你的帮助!所以你对在出口调用函数不感兴趣?这只是在事件循环中使用它们的副作用?在这种情况下,我将编写一个C或C++的适配器,它将包装每个Python回调。库调用包装器,包装器调用已注册的回调,或者,如果关联的回调已取消注册,则只返回。提供手动注销回调的函数,以及注销所有回调的函数。然后使用Python的
atexit
模块调用后者。不,事件循环在常规操作期间(耶!)和关闭
atexit
(boo!)时都调用已注册的回调。总之,我需要两者兼而有之。
callback_type = ctypes.CFUNCTYPE(None)
wrapped_callback = callback_type(callback)
shared.set_callback(wrapped_callback)