【问题标题】:How do I prevent python deallocating ctypes callbacks at exit-time?如何防止 python 在退出时释放 ctypes 回调?
【发布时间】:2015-07-24 14:51:47
【问题描述】:

假设我有以下共享库要由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 的 atexit 模块而不是使用 C 库注册回调吗?如果不是,您需要为回调编译一个小型库。在调用 C atexit 函数时,Python 解释器早已被拆除。
  • 遗憾的是,回调是库中该部分(事件循环)中相当不可或缺的一部分。我正在实习,所以我会问我的主管,当这种情况(很少)在星期一发生时,他们想做什么。感谢您的帮助!
  • 所以你对在退出时调用函数真的不感兴趣吗?这只是将它们与事件循环一起使用的副作用吗?在那种情况下,我会用 C 或 C++ 编写一个适配器来包装每个 Python 回调。库调用包装器,它要么调用已注册的回调,要么,如果关联的回调已取消注册,则只返回。提供手动取消注册回调的函数,以及取消注册所有回调的函数。然后使用Python的atexit模块调用后者。
  • 不,事件循环在常规操作期间(耶!)和关闭时调用注册的回调atexit(嘘!)。总之,我需要两者都做。

标签: python callback ctypes atexit


【解决方案1】:

我在这里参加聚会可能会迟到,但我最近遇到了类似的问题。基本上,回调是由 Python 进行垃圾收集的。如果你这样做:

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

应该可以解决段错误。

【讨论】:

    猜你喜欢
    • 2011-09-20
    • 2023-01-27
    • 2010-10-09
    • 2023-04-06
    • 2023-03-17
    • 2017-05-08
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多