python - gc 上的段错误,使用 ctypes

标签 python c segmentation-fault ctypes

我正在使用 ctypes 调用 native c 库,传入一个专门的结构并取回另一个结构。调用 gc 时出现段错误,无法在 gdb 跟踪中找到更多详细信息:

Thread 1 "python" received signal SIGSEGV, Segmentation fault.
0x00007ffff7a5ca44 in visit_decref (op=0xf5ee10, data=0x0) at Modules/gcmodule.c:374
374 Modules/gcmodule.c: No such file or directory.
gdb) where
#0  0x00007ffff7a5ca44 in visit_decref (op=0xf5ee10, data=0x0) at Modules/gcmodule.c:374
#1  0x00007ffff798bb0c in subtype_traverse (self=0x7fff7744fd90, visit=0x7ffff7a5ca40 <visit_decref>, arg=0x0) at Objects/typeobject.c:1011
#2  0x00007ffff7a5bd77 in subtract_refs (containers=<optimized out>) at Modules/gcmodule.c:399
#3  collect (generation=generation@entry=0, n_collected=n_collected@entry=0x7fffffffb708, n_uncollectable=n_uncollectable@entry=0x7fffffffb710, nofail=nofail@entry=0) at Modules/gcmodule.c:956
#4  0x00007ffff7a5c95d in collect_with_callback (generation=0) at Modules/gcmodule.c:1128
#5  0x00007ffff7a5d1eb in collect_generations () at Modules/gcmodule.c:1151
#6  _PyObject_GC_Alloc (basicsize=<optimized out>, use_calloc=0) at Modules/gcmodule.c:1726
#7  _PyObject_GC_Malloc (basicsize=<optimized out>) at Modules/gcmodule.c:1736

代码示例是这样的:

_C_DOUBLE_P = POINTER(c_double)
_C_INT_P = POINTER(c_int)


class _IN_DATA(Structure):
    _fields_ = [('in_kps', _C_DOUBLE_P),
                ('in_desc', _C_DOUBLE_P)
                ]
    def __init__(self):
        t = c_int
        self.test = ctypes.cast(t, POINTER(c_int))  

class _OUT_DATA(Structure):
    _fields_ = [
        ('num_out_kps', ctypes.c_int),
        ('out_kps', _C_DOUBLE_P),  
        ('out_desc', _C_DOUBLE_P)
    ]

class _IN_DATA_LIST(ctypes.Structure):
    _fields_ = [
        ('num_crops', c_int),
        ('crops', POINTER(_IN_DATA))
    ]


    def __init__(self, crops_raw_kps: List[np.ndarray], crops_raw_descriptors: List[np.ndarray]):
        num_crops = len(crops_raw_kps)
        self.num_crops = num_crops
        crops = (POINTER(_IN_DATA) * num_crops)()
        self.crops = ctypes.cast(crops, POINTER(_IN_DATA))
        for i in range(num_crops):
            self.crops[i].in_kps = crops_raw_kps[i].ctypes.data_as(_C_DOUBLE_P)
            self.crops[i].in_desc = crops_raw_descriptors[i].ctypes.data_as(_C_DOUBLE_P)

class _OUT_DATA_LIST(ctypes.Structure):
    _fields_ = [
        ('crops_data', ctypes.POINTER(_OUT_DATA)),
        ('num_results', c_int)
    ]  

class SPPostWrapper:

    def __init__(self):

        self._post_processor_lib = ctypes.cdll.LoadLibrary("multitracker/custom_features/build/ffme/libffme.so")
        self._post_processor_lib.py_postprocess.restype = _OUT_DATA_LIST
        self._post_processor_lib.py_postprocess.argtypes = [POINTER(_IN_DATA_LIST)]

    def post_process_multi(self, crops_raw_kps: List[np.ndarray], crops_raw_descriptors: List[np.ndarray]):
        num_crops = len(crops_raw_kps)
        adjusted_kps = [np.asarray(np.squeeze(kp), np.double) for kp in crops_raw_kps]
        adjusted_desc = [np.asarray(np.squeeze(desc), np.double) for desc in crops_raw_descriptors]
        crops_struct = _IN_DATA_LIST(adjusted_kps, adjusted_desc)
        out_result= self._post_processor_lib.py_postprocess(ctypes.byref(crops_struct))

我在 python 中分配了 IN_DATA,在 c 代码中分配了 OUT_DATA,我尝试在 c 中使用缓存(假设 python 不会清理内存)或为每次调用分配新内存(假设 python 确实释放了 out_data 内存) - 当调用 python 的 gc 时,这两种方法都失败。

更新: 为了更好地隔离问题,我删除了 out_data 的使用,将方法设置为 void,但问题仍然存在。我还尝试将 in 数据保留为成员,我认为这可以防止数据发生,直到进程关闭。所以这一定与我分配给输入/输入列表的内存有关。

更新 2: 我能够验证只有当我在 IN_DATA_LIST 中传递超过 1 个项目(超过 1 个裁剪)时才会发生问题。这确实很奇怪......

最佳答案

在验证即使 c 代码不执行任何操作且不返回任何内容且无法解决该问题后,我最终使用了更浅的输入(和输出),这似乎运行正常,没有段错误:

class _IN_DATA(Structure):
    _fields_ = [
                ('num_crops', c_int),
                ('in_kps', _C_DOUBLE_P),
                ('in_desc', _C_DOUBLE_P)
                ]
    def __init__(self, kps_flat, desc_falt, num_crops):
        self.num_crops = num_crops
        self.in_kps = kps_flat.ctypes.data_as(_C_DOUBLE_P)
        self.in_desc = desc_falt.ctypes.data_as(_C_DOUBLE_P)


class _OUT_DATA(Structure):
    _fields_ = [
        ('num_results', ctypes.c_int),
        ('results_per_crop', _C_INT_P),
        ('out_kps', _C_DOUBLE_P),  # x,y, score
        ('out_desc', _C_DOUBLE_P)
    ]

关于python - gc 上的段错误,使用 ctypes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57707726/

相关文章:

c - 获取字符数组的长度

c - 为什么在一切都正确完成后我会在应用程序结束时出现段错误?

android - 防止 WebView 中的 Flash Player 故障,就像 Android 浏览器所做的那样

python - 减去python中的匹配行

c - 函数内数组大小计算错误

python - 如何使用 Python 从文本文件中删除回车符?

c - 在C中实现 vector (动态数组) - 函数和主文件

c - 在 C 中分割字符串会导致段错误(核心转储)

python - 如何使用 'serial.tools.list_ports' python 模块获取可用串口列表?

python - PyCharm:在 Python 控制台中执行