我正在使用返回Buffer对象的二进制Python库。该对象基本上是C对象的包装,其中包含指向实际内存缓冲区的指针。我需要的是从Python获取该指针中包含的内存地址,问题是Buffer对象没有获取它的Python方法,因此我需要做一些技巧来获取它。
目前,我发现了一种获取指针值的丑陋且不安全的方法:
我知道C对象的内部结构:
typedef struct _Buffer {
PyObject_VAR_HEAD PyObject *parent;
int type; /* GL_BYTE, GL_SHORT, GL_INT, GL_FLOAT */
int ndimensions;
int *dimensions;
union {
char *asbyte;
short *asshort;
int *asint;
float *asfloat;
double *asdouble;
void *asvoid;
} buf;
} Buffer;
因此,我编写了以下Python代码:
# + PyObject_VAR_HEAD size
# + 8 bytes PyObject_VAR_HEAD PyObject *parent
# + 4 bytes from int type
# + 4 bytes from int ndimensions
# + 8 bytes from int *dimensions
# = 24
offset = sys.getsizeof(0) + 24
buffer_pointer_addr = id(buffer) + offset
buffer_pointer_data = ctypes.string_at(buffer_pointer_addr, 8)
buffer_pointer_value = struct.unpack('Q', buffer_pointer_data)[0]
这一直在为我工作。如您所见,我使用来获取Python Buffer对象的内存地址id(buffer)
,但您可能知道这并不是指向缓冲区的实际指针,而仅仅是CPython中恰好是该Python对象的内存地址的Python数字。
因此,然后我添加了通过添加C结构中所有变量的大小而计算出的偏移量。我正在对字节大小(显然是完全不安全的)进行硬编码,除了PyObject_VAR_HEAD是我得到的sys.getsizeof(0)
。
通过添加偏移量,我获得了包含指向实际缓冲区的指针的内存地址,然后使用ctypes对其进行提取ctypes.string_at
,并将指针的大小硬编码为8个字节(我在64位OS上),然后使用struct.unpack
转换将其转换为实际的Python int
。
所以现在我的问题是:如何在不对所有大小进行硬编码的情况下实现更安全的解决方案?(如果存在)。也许有ctypes吗?如果它仅适用于CPython,就可以了。
在研究了C Struct填充并基于以下假设之后,我找到了一个更安全的解决方案:
union{}
是C结构中最大的指针。无论如何,在大多数现代OS上,数据指针类型之间不会有不同的大小。基于所有这些假设和此处找到的规则:https : //stackoverflow.com/a/38144117/8861787,我们可以放心地说,该结构的末尾将没有填充,并且我们可以提取指针而无需对任何内容进行硬编码:
# Get the size of the Buffer Python object
buffer_obj_size = sys.getsizeof(buffer)
# Get the size of void * C-type
buffer_pointer_size = ctypes.sizeof(ctypes.c_void_p)
# Calculate the address to the pointer assuming that it's at the end of the C Struct
buffer_pointer_addr = id(buffer) + buffer_obj_size - buffer_pointer_size
# Get the actual pointer value as a Python Int
buffer_pointer_value = (ctypes.c_void_p).from_address(buffer_pointer_addr).value
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句