我有从Windows移植的库,我使用ctypes从Python调用。这个组合在32位和64位Windows上运行,使用python 2.7和3.5,但在移植到我当前的Linux环境后使用段错误,使用gcc 6.3和python 3.5运行Ubuntu 17.04。
该库有两个回调,签名略有不同。第一个按预期运行,但是在ctypes库中第二个段错误。
C中的回调签名是:
typedef void (*StatusCallback)(void* ctxt, enum STATUS status, void* data);
typedef void (*MsgCallback)(void* ctxt, enum STREAM stream, DataMsg* msg);
并使用ctypes定义为
STATUSCB = CFUNCTYPE(None, c_void_p, c_uint, c_void_p)
MSGCB = CFUNCTYPE(None, c_void_p, c_uint, POINTER(DataMsg))
它是第二个失败的回调,它与它的最后一个参数(即POINTER(DataMsg))中的第一个不同。在C DataMsg中定义为:
typedef struct DataItem
{
uint8_t* name;
enum DATATYPE type;
uint8_t* unit;
uint32_t numValues;
void* values;
enum DATASTATUS* valueStatuses;
uint32_t numChildren;
struct DataItem* children;
}
DataItem;
typedef struct DataMsg
{
uint64_t timestamp;
uint32_t numItems;
struct DataItem* items;
}
DataMsg;
在python中:
DataItemPtr = POINTER(DataItem)
DataItem._fields_ = [
('name', c_char_p),
('type', c_uint),
('unit', c_char_p),
('numValues', c_uint),
('values', c_void_p),
('valueStatuses', POINTER(c_uint)),
('numChildren', c_uint),
('children', DataItemPtr) ]
DataMsg._fields_ = [
('timestamp', c_ulonglong),
('numItems', c_uint),
('items', DataItemPtr) ]
这是在gdb下运行时获得的堆栈跟踪:
Thread 5 "python3" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffec7ba700 (LWP 17002)]
0x00007ffff66b4b8e in ?? ()
from /usr/lib/python3.5/lib-dynload/_ctypes.cpython-35m-x86_64-linux-gnu.so
(gdb) bt
#0 0x00007ffff66b4b8e in ?? ()
from /usr/lib/python3.5/lib-dynload/_ctypes.cpython-35m-x86_64-linux-gnu.so
#1 0x00007ffff66b4f4c in ?? ()
from /usr/lib/python3.5/lib-dynload/_ctypes.cpython-35m-x86_64-linux-gnu.so
#2 0x00007ffff66b57b7 in ffi_closure_unix64_inner ()
from /usr/lib/python3.5/lib-dynload/_ctypes.cpython-35m-x86_64-linux-gnu.so
#3 0x00007ffff66b5c10 in ffi_closure_unix64 ()
from /usr/lib/python3.5/lib-dynload/_ctypes.cpython-35m-x86_64-linux-gnu.so
#4 0x00007fffe801972c in HandleSysDescriptor (Channel=0x7fffe000f3a0,
channel=0x7fffe000f570, flow_id=2, dict=0x7fffe0024368, msg=0x7fffe0020470)
at /home/michael/dev//linux/source/ARCDevKit/SapphireReceive.c:471
#5 0x00007fffe8018fb2 in HandleMessage (channel=0x7fffe000f570, flow_id=2,
dict=0x7fffe0024368, msg=0x7fffe0020470)
at /home/michael/dev//linux/source/ARCDevKit/SapphireReceive.c:290
#6 0x00007fffe8024860 in process_item (channel=0x7fffe000f570, flow_id=2,
dict=0x7fffe0024368, mesg=0x7fffe0020470)
at /home/michael/dev//linux/source/SbxCLib/sbxc/usnet/us_tcp_channel_connection.c:97
我尝试用c_void_p替换POINTER(DataMsg)以查看是否可以获得回调以输入python代码,但是发生了同样的错误。我担心枚举的大小与用于枚举参数类型的c_uint有关,但它适用于第一次回调,而不是第二次回调。
直接从C调用它是有效的,这是怀疑我使用ctypes的另一个原因。
它也可以在Windows下使用许多不同的VS版本(2005年,2008年,2013年,2015年)进行多种不同的排列,因此我怀疑在处理指针,枚举或整数方面存在某种差异,但我还没有找到任何特定的东西。