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