我有一个c库,需要一些回调, 它们在链表中处理。
python callable是那些:
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ültigerMaschinenbefehl”(翻译:无效的处理器指令或类似的东西)
所以我只是不知道如何解决它。
这是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)
答案 0 :(得分:1)
在这里,您有指向struct _SFRCommandHolder
的指针,但数据存在于何处?您在哪里分配了struct _SFRCommandHolder
?
如果响应是&#34;无处&#34;,您的代码具有未定义的行为,因为sfr_comms可能具有任何值(尤其是非NULL值);这导致curr->com
几乎每次都会导致分段错误。
答案 1 :(得分:1)
问题是,python垃圾收集会删除 没有(强烈)引用它们的对象。
来自https://docs.python.org/3/library/ctypes.html#callback-functions
请注意
确保保留对CFUNCTYPE()对象的引用,只要它们是从C代码中使用的。 ctypes没有,如果你不这样做,它们可能被垃圾收集,在回调时崩溃你的程序。
另外,请注意,如果在Python控件之外创建的线程中调用回调函数(例如,通过调用回调的外部代码),ctypes会在每次调用时创建一个新的虚拟Python线程。对于大多数用途,此行为是正确的,但这意味着使用threading.local存储的值将无法在不同的回调中存活,即使这些调用是从同一个C线程进行的。
似乎仅使用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)