ctypes 回调函数抛出 SIGSEGV
ctypes callback function throws SIGSEGV
我有一个 c 库,需要一些回调,
它们在链表中处理。
可调用的 python 是那些:
def callback_exit():
print("exiting.")
sys.exit(0)
# never reached
return c_int(0)
def hw_print_int():
print("foo")
return c_int(0)
我这样将它们添加到列表中:
SFR_COMM=CFUNCTYPE(c_voidp)
class MyClass:
def add_SFR_callback(self,operation_number,callback):
all_callbacks=c_voidp.in_dll(self.memlib,"sfr_comms")
my_callback=self.memlib.newSFRCommand(c_uint(operation_number),callback)
new_all_callbacks=self.memlib.new_SFRCommandHolder(my_callback,all_callbacks)
self.memlib.set_sfr_comms(new_all_callbacks)
my_class_object.add_SFR_callback(0xff,SFR_COMM(callback_exit))
my_class_object.add_SFR_callback(0xff,SFR_COMM(hw_print_int))
这工作正常,直到回调被调用,然后我只收到一个 SIGSEGV。
重要:SIGSEGV 告诉我,它是一个 "Ungültiger Maschinenbefehl"(翻译:无效处理器指令 或类似的东西)
所以我不知道该如何解决。
这是 C 代码:
struct _SFRCommandHolder * sfr_comms;
#define DEBUG
unsigned int SpecialFunctionRegister_exec(unsigned int val)
{
struct _SFRCommandHolder * curr=sfr_comms;
unsigned int ret=-1;
while (curr!=NULL)
{
#ifdef DEBUG
printf("( %zd => %zd => %zd ) %u ?= %u",curr,curr->com,curr->com->funct,curr->com->val,val);
#endif
if(curr->com->val==val)
{
#ifdef DEBUG
printf("\t\tTRUE\n");
#endif
ret=curr->com->funct(); // <= SIGSEGV here
#ifdef DEBUG
printf("callback done.\n");
#endif
}
#ifdef DEBUG
else
{
printf("\t\tFALSE\n");
}
#endif
curr=curr->next;
}
return ret;
}
我不认为 sys.exit
是个问题,因为它在几次提交之前就可以正常工作。
编辑:
调用 hw_print_int
工作正常,但 callback_exit
不工作。
顺便说一句:如果我不添加 hw_print_int
,callback_exit
也有效
输出:
( 13185760 => 13136448 => 139994994819144 ) 3 ?= 255 FALSE
( 13038864 => 13034576 => 139994994819088 ) 255 ?= 255 TRUE
Ungültiger Maschinenbefehl (Speicherabzug geschrieben)
在这里,您有指向 struct _SFRCommandHolder
的指针,但数据存储在哪里?你在哪里分配了一个struct _SFRCommandHolder
?
如果响应是 "nowhere",您的代码有未定义的行为,因为 sfr_comms 可能有 any 值(尤其是非 NULL 值);这导致 curr->com
几乎每次都导致分段错误。
问题是,python 垃圾回收删除了
没有(强)引用的对象。
来自https://docs.python.org/3/library/ctypes.html#callback-functions
Note
Make sure you keep references to CFUNCTYPE() objects as long as they are used from C code. ctypes doesn’t, and if you don’t, they may be garbage collected, crashing your program when a callback is made.
Also, note that if the callback function is called in a thread created outside of Python’s control (e.g. by the foreign code that calls the callback), ctypes creates a new dummy Python thread on every invocation. This behavior is correct for most purposes, but it means that values stored with threading.local will not survive across different callbacks, even when those calls are made from the same C thread.
使用 struct _SFRCommandHolder *
.
来引用它们似乎是不够的
所以添加另一个参考就足够了:
class MyClass:
def __init__(self,*args):
# ...
self.refs=[]
def add_SFR_callback(self,operation_number,callback):
all_callbacks=c_voidp.in_dll(self.memlib,"sfr_comms")
my_callback=self.memlib.newSFRCommand(c_uint(operation_number),callback)
new_all_callbacks=self.memlib.new_SFRCommandHolder(my_callback,all_callbacks)
self.memlib.set_sfr_comms(new_all_callbacks)
self.refs.append(callback)
我有一个 c 库,需要一些回调, 它们在链表中处理。
可调用的 python 是那些:
def callback_exit():
print("exiting.")
sys.exit(0)
# never reached
return c_int(0)
def hw_print_int():
print("foo")
return c_int(0)
我这样将它们添加到列表中:
SFR_COMM=CFUNCTYPE(c_voidp)
class MyClass:
def add_SFR_callback(self,operation_number,callback):
all_callbacks=c_voidp.in_dll(self.memlib,"sfr_comms")
my_callback=self.memlib.newSFRCommand(c_uint(operation_number),callback)
new_all_callbacks=self.memlib.new_SFRCommandHolder(my_callback,all_callbacks)
self.memlib.set_sfr_comms(new_all_callbacks)
my_class_object.add_SFR_callback(0xff,SFR_COMM(callback_exit))
my_class_object.add_SFR_callback(0xff,SFR_COMM(hw_print_int))
这工作正常,直到回调被调用,然后我只收到一个 SIGSEGV。
重要:SIGSEGV 告诉我,它是一个 "Ungültiger Maschinenbefehl"(翻译:无效处理器指令 或类似的东西)
所以我不知道该如何解决。
这是 C 代码:
struct _SFRCommandHolder * sfr_comms;
#define DEBUG
unsigned int SpecialFunctionRegister_exec(unsigned int val)
{
struct _SFRCommandHolder * curr=sfr_comms;
unsigned int ret=-1;
while (curr!=NULL)
{
#ifdef DEBUG
printf("( %zd => %zd => %zd ) %u ?= %u",curr,curr->com,curr->com->funct,curr->com->val,val);
#endif
if(curr->com->val==val)
{
#ifdef DEBUG
printf("\t\tTRUE\n");
#endif
ret=curr->com->funct(); // <= SIGSEGV here
#ifdef DEBUG
printf("callback done.\n");
#endif
}
#ifdef DEBUG
else
{
printf("\t\tFALSE\n");
}
#endif
curr=curr->next;
}
return ret;
}
我不认为 sys.exit
是个问题,因为它在几次提交之前就可以正常工作。
编辑:
调用 hw_print_int
工作正常,但 callback_exit
不工作。
顺便说一句:如果我不添加 hw_print_int
,callback_exit
也有效
输出:
( 13185760 => 13136448 => 139994994819144 ) 3 ?= 255 FALSE
( 13038864 => 13034576 => 139994994819088 ) 255 ?= 255 TRUE
Ungültiger Maschinenbefehl (Speicherabzug geschrieben)
在这里,您有指向 struct _SFRCommandHolder
的指针,但数据存储在哪里?你在哪里分配了一个struct _SFRCommandHolder
?
如果响应是 "nowhere",您的代码有未定义的行为,因为 sfr_comms 可能有 any 值(尤其是非 NULL 值);这导致 curr->com
几乎每次都导致分段错误。
问题是,python 垃圾回收删除了 没有(强)引用的对象。
来自https://docs.python.org/3/library/ctypes.html#callback-functions
Note
Make sure you keep references to CFUNCTYPE() objects as long as they are used from C code. ctypes doesn’t, and if you don’t, they may be garbage collected, crashing your program when a callback is made.
Also, note that if the callback function is called in a thread created outside of Python’s control (e.g. by the foreign code that calls the callback), ctypes creates a new dummy Python thread on every invocation. This behavior is correct for most purposes, but it means that values stored with threading.local will not survive across different callbacks, even when those calls are made from the same C thread.
使用 struct _SFRCommandHolder *
.
所以添加另一个参考就足够了:
class MyClass:
def __init__(self,*args):
# ...
self.refs=[]
def add_SFR_callback(self,operation_number,callback):
all_callbacks=c_voidp.in_dll(self.memlib,"sfr_comms")
my_callback=self.memlib.newSFRCommand(c_uint(operation_number),callback)
new_all_callbacks=self.memlib.new_SFRCommandHolder(my_callback,all_callbacks)
self.memlib.set_sfr_comms(new_all_callbacks)
self.refs.append(callback)