我正在使用ctypes调用本机c库,传入一个专门的结构并返回另一个结构。调用 gc 时出现 seg 错误,在 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,OUT_DATA在 c 代码中分配,我尝试在 c 中使用缓存(假设 python 不会清理内存(或为每个调用分配新内存(假设 python 确实释放了out_data内存( - 当调用 python 的 gc 时,这两种方法都会失败。
更新:为了更好地隔离问题,我删除了out_data的使用,将方法设置为 void,但问题仍然发生。我还尝试将数据保留为成员,我认为这可以防止数据发生,直到进程关闭。所以它一定与我分配给输入/输入列表的内存有关。
更新 2:我能够验证问题仅在IN_DATA_LIST中传递超过 1 个项目(超过 1 个裁剪(时才会发生。这确实很奇怪...
在验证即使 c 代码不执行任何操作并且不返回任何内容也会出现问题并且无法解决它之后,我最终使用了更浅的输入(和输出(,这似乎运行正常,没有 seg 错误:
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)
]