【问题标题】:python bytes object underlying buffer reallocation, cffipython字节对象底层缓冲区重新分配,cffi
【发布时间】:2022-01-07 07:32:36
【问题描述】:

我正在使用 python 字节对象将一些数据传递给使用 CFFI 库的本机实现的方法,例如:

from cffi import FFI
ffi = FFI()
lib = ffi.dlopen(libname)
ffi.cdef("""
    void foo(char*);
""")
x = b'abc123'
lib.foo(x)

据我了解,本机方法接收的指针是 x 字节对象后面的实际底层缓冲区的指针。这在 99% 的情况下都可以正常工作,但有时指针似乎变得无效,并且它指向的数据包含垃圾,在本机调用完成后的一段时间 - 本机代码在从初始调用返回后保留指针并期望要存在的数据,python 代码确保保留对 x 的引用,以便指针保持有效。

在这些情况下,如果我再次调用具有相同字节对象的本机方法,我可以看到我得到一个指向相同值但位于不同地址的不同指针,这表明字节对象后面的底层缓冲区已经移动(如果我关于 CFFI 提取指向字节对象包含的底层数组的指针的假设是正确的,并且没有在任何地方创建临时副本),即使据我所知,字节对象还没有被修改无论如何(代码是大型代码库的一部分,但我有理由确定字节对象不会被代码直接修改)。

这里会发生什么?我对 CFFI 获取指向字节对象的实际内部缓冲区的指针的假设是否不正确?出于垃圾收集/内存压缩的原因,python是否可能允许以静默方式重新分配字节对象后面的缓冲区,这是否不知道我持有指向它的指针?我正在使用 pypy 而不是默认的 python 解释器,如果这会有所不同的话。

【问题讨论】:

    标签: python byte buffer pypy cffi


    【解决方案1】:

    你的猜测是正确的答案。 (记录的)保证只是在这种情况下传递的指针在调用期间有效。

    PyPy 的垃圾收集器可以移动内存中的对象,如果它们足够小,那么这样做可以提高整体性能。但是,在进行这样的 cffi 调用时,pypy 通常会在调用期间将对象标记为“固定”(除非固定对象已经太多并且添加更多会严重影响未来的 GC 性能;在这种罕见的情况下,它会无论如何都要复制一份,然后释放它)。

    如果您的 C 代码需要在调用返回后访问内存,您必须显式地制作一个副本,例如使用 ffi.new("char[]", mybytes),并在需要时让它保持活动状态。

    【讨论】:

    • 谢谢。你能指出任何相关文件的方向吗? (需要为政治斗争提供备份以更改某些代码。)
    • 你说得对,文档并不清楚。我会修好它。此时需要从cffi.readthedocs.io/en/latest/ref.html#conversions点“[1]”推导出来:如果调用函数f(char*),则相当于f(char[]),所以调用f(b"foo")大致变成f(ffi.new("char[]", b"foo")) ,这意味着调用完成后字符数组消失。最后一段提到了对字符串的优化;我将对其进行修复以澄清此优化并不意味着 char* 在调用后仍然有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多