【问题标题】:ctypes callback function throws SIGSEGVctypes 回调函数抛出 SIGSEGV
【发布时间】:2015-11-07 21:14:16
【问题描述】:

我有一个 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)

【问题讨论】:

  • 尝试将 sfr_comms 初始化为 NULL。
  • @cup 这不是问题。我的记忆处理是有效的。 (我测试过)

标签: c python-3.x callback segmentation-fault shared-libraries


【解决方案1】:

在这里,您有指向struct _SFRCommandHolder 的指针,但数据在哪里?你在哪里分配了struct _SFRCommandHolder

如果响应是“无处”,则您的代码具有未定义的行为,因为 sfr_comms 可能具有 any 值(尤其是非 NULL 值);这导致curr-&gt;com 几乎每次都会导致分段错误。

【讨论】:

  • struct _SFRCommandHolder 和回调都是有效的 ptr。 (请参阅:我打印了它们)在new_SFRCommandHolder 我为struct _SFRCommandHolder 分配空间。并且:如果我打电话给hw_print_int 它可以工作。
  • 您确定 sys.exit() 可用吗?如果你从回调中移除这个调用,它还会崩溃吗?
  • sys.exit 不会改变任何东西。是不是,python 删除了我的函数对象?
  • 好吧,看看 LittleByBlue 的回答
  • 好吧,就是我! (我测试过)谢谢你的帮助。 +1 提出(一般)正确的问题。
【解决方案2】:

问题是,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)

【讨论】:

    猜你喜欢
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多