Предположим, у меня есть следующая общая библиотека, которую нужно загрузить с помощью 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 (которую я не могу изменить), содержащей несколько обратных вызовов, вызываемых во время выхода. В настоящее время они вызывают ошибки сегментации при выходе из тестовой программы. Можно ли предотвратить это?
atexit
вместо регистрации обратных вызовов в библиотеке C? Если нет, вам нужно будет скомпилировать небольшую библиотеку для обратных вызовов. Интерпретатор Python уже давно разрушен к моменту вызова функций Catexit
. - person Eryk Sun   schedule 24.07.2015atexit
для вызова последнего. - person Eryk Sun   schedule 25.07.2015atexit
(бу!). В общем, мне нужно сделать и то, и другое. - person Michael Rawson   schedule 25.07.2015