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

问题描述 投票:3回答:1

假设我有以下的共享库,要由 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);
}

假设我通过神奇的 "回调 "来加载这个库 ctypes:

import ctypes
shared = ctypes.CDLL('./test.so')

#a callback function
def callback():
    print "callback invoked"

#register functions to run at exit
shared.init()
#set the callback function to invoke
shared.set_callback(ctypes.CFUNCTYPE(None)(callback))
#invoke the callback function
shared.invoke_callback()

#...callback also invoked here, right?

我期望这个结果会是如下所示。

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 ()

现在,我的问题是: 我的问题是 其实 试图绑定到一个大型的C代码库(我无法修改),其中包含几个在退出时调用的回调。这些回调目前在测试程序退出时造成了分段故障。有可能防止这种情况发生吗?

python callback ctypes atexit
1个回答
1
投票

我可能来晚了,但我最近遇到了一个类似的问题。基本上,回调被Python收集了垃圾。如果你这样做。

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

它应该可以解决segfault的问题

© www.soinside.com 2019 - 2024. All rights reserved.