python-3.x - ctypes 对象位于非常相似的内存地址

标签 python-3.x ctypes

当我意识到这个有趣的行为时,我正在调试另一个项目:

如果我从 python 可调用对象生成两个 c 可调用对象,它们总是位于非常相似的位置:

from ctypes import *

def foo():
    print("foo")
def bar():
    print("bar")
c_cm=CFUNCTYPE(c_voidp)

c_foo=c_cm(foo)
print(c_foo)
c_bar=c_cm(bar)
print(c_bar)

运行几次:

<CFunctionType object at 0x7f8ddb65d048>
<CFunctionType object at 0x7f8ddb65d110>

<CFunctionType object at 0x7f40a022e048>
<CFunctionType object at 0x7f40a022e110>

<CFunctionType object at 0x7fa1f1fb1048>
<CFunctionType object at 0x7fa1f1fb1110>

7f 不是有趣的部分,而是 048110

这是否意味着我的程序始终位于ram中非常相似的位置?

信息:我使用的是 linux 3.18.x

最佳答案

CPython 的小对象分配器使用 256 KB 的区域,分为 4 KB 的池,其中给定的池专用于特定的分配大小(范围从 8 到 512 字节,步长为 8)。地址的低 3 位十六进制数字(12 位)是对象在池中的偏移量。此设计在 Objects/obmalloc.c 中的大量评论中进行了讨论。 .

对于 64 位 Linux,一个 ctypes 函数指针对象为 200 (0xc8) 字节,即 sys.getsizeof(c_bar) == 200 ,因此一个池可以容纳 20 个函数指针。请注意,池中第一个分配的对象位于偏移量 0x048 而不是 0x000。池本身有一个 48 (0x030) 字节的初始 header ( pool_header ),另外每个 ctypes 对象都有一个 24 (0x018) 字节的垃圾收集 header ( PyGC_Head )。如果没有 GC header ,ctypes 函数指针为 176 字节 (0x0b0)。因此,下一个函数指针的 GC header 位于偏移量 0x0f8 处,对象本身在 24 个字节后的偏移量 0x110 处开始。

一旦开始从完全空闲的池中分配,您就可以打印一堆来查看模式。例如,funcs = [c_cm(foo) for i in range(10000)][-40:]; idx = 0; while id(funcs[idx]) & 0xfff != 0x048: idx +=1; print(*[funcs[n] for n in range(idx, idx+20)], sep='\n') :

<CFunctionType object at 0x7f66ca3df048>
<CFunctionType object at 0x7f66ca3df110>
<CFunctionType object at 0x7f66ca3df1d8>
<CFunctionType object at 0x7f66ca3df2a0>
<CFunctionType object at 0x7f66ca3df368>
<CFunctionType object at 0x7f66ca3df430>
<CFunctionType object at 0x7f66ca3df4f8>
<CFunctionType object at 0x7f66ca3df5c0>
<CFunctionType object at 0x7f66ca3df688>
<CFunctionType object at 0x7f66ca3df750>
<CFunctionType object at 0x7f66ca3df818>
<CFunctionType object at 0x7f66ca3df8e0>
<CFunctionType object at 0x7f66ca3df9a8>
<CFunctionType object at 0x7f66ca3dfa70>
<CFunctionType object at 0x7f66ca3dfb38>
<CFunctionType object at 0x7f66ca3dfc00>
<CFunctionType object at 0x7f66ca3dfcc8>
<CFunctionType object at 0x7f66ca3dfd90>
<CFunctionType object at 0x7f66ca3dfe58>
<CFunctionType object at 0x7f66ca3dff20>

请注意,repr 中打印的函数指针对象的基地址与传递给 C 库的地址没有直接关系。函数指针对象(即 PyCFuncPtrObject )有一个 b_ptr字段指向一个缓冲区,该缓冲区保存传递给 C 的实际函数地址。您可以通过创建 void * 来检查该值。来自函数指针的指针,例如addr_bar = c_void_p.from_buffer(c_bar).value 。对于回调,ctypes 分配一 block 可执行内存,在其中写入一些设置调用 closure_fcn 的代码。调用目标Python函数。这是 CThunkObject ,它被引用(保持事件状态),例如 c_foo._objects['0'] .

关于python-3.x - ctypes 对象位于非常相似的内存地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33581455/

相关文章:

python - 从网站结果中抓取表格为空

python - datetime.fromtimestamp() 比 datetime.utcfromtimestamp() 花费的时间更长

java - 在一个项目中集成 Java 和 Python 3.x 代码

python - ctypes找不到find dll函数

Python Ctypes 和线程

python - 是否有代码可以在程序中运行以使用已知密码连接到安全 WiFi

python-3.x - Python3 f 字符串 : how to avoid having to escape literal curly brackets?

c++ - ctypes/C++ 段错误访问成员变量

python ctypes uint8缓冲区使用

python - 如何在 python ctypes 中包装包含 C++ 对象的 C 结构