【问题标题】:Python C Module - Malloc fails in specific version of PythonPython C 模块 - Malloc 在特定版本的 Python 中失败
【发布时间】:2014-02-22 21:26:20
【问题描述】:

我正在编写一个 Python 模块来在 O_DIRECT 上下文中执行 IO。 O_DIRECT 的限制之一是,对于 2.4 和 2.5 内核,您必须读入在 4096 字节边界上对齐的缓冲区,而 2.6 及更高版本将接受 512 的任何倍数。

明显的内存分配候选者是posix_memalign(void **memptr, size_t alignment, size_t size)

在我的代码中,我这样分配一个区域:

char *buffer = NULL;

int mem_ret = posix_memalign((void**)&buffer, alignment, size);

if (!buffer) {
    PyErr_NoMemory();
    return NULL;
}

/* I do some stuff here */

free(buffer);

当我使用 python3.2 编译和导入模块时,这个(以及其他未显示的模块)工作正常。

当我尝试对 python2.7 进行相同操作时(我想保持兼容性),它会抛出 PyErr_NoMemory 异常和mem_ret == ENOMEM,表明它无法分配。

为什么我编译的 Python 版本会影响 posix_memalign 的运行方式?

操作系统:Ubuntu 12.04 LTS

编译器:Clang + GCC 显示相同的行为

更新

感谢 user694733,我现在有了一段工作代码
然而,它的工作原理让我更加困惑:

#if PY_MAJOR_VERSION >= 3
char *buffer = NULL;

int mem_ret = posix_memalign((void**)&buffer, alignment, count);
#else
void *mem = NULL;

int mem_ret = posix_memalign(&mem, alignment, count);

char *buffer = (char*)mem;
#endif

谁能解释为什么 incorrect 第一个块在 Python3 下工作,但不是 2.7,更重要的是为什么 正确 第二个块在 Python3 下不工作?

更新 2

情节变厚了,确定了下面代码的正确形式后,我在 4 个不同版本的 Python 上进行了测试。

void *mem = NULL;

int mem_ret = posix_memalign(&mem, alignment, count);

char *buffer = (char*)mem;

if (!buffer) {
    PyErr_NoMemory();
    return NULL;
}

/* Do stuff with buffer */

free(buffer);

Python 2.7 下:此代码按预期运行。
Python 3.1 下:此代码按预期运行。
Python 3.2 下:此代码生成 mem_ret == ENOMEM 并为 buffer 返回 NULL
Python 3.3 下:此代码按预期运行。

未包含在 Ubuntu 存储库中的 Python 版本是从位于 https://launchpad.net/~fkrull/+archive/deadsnakes 的 PPA 安装的

如果要相信标记为 Python 二进制文件的版本,我安装的版本是:

python2.7 
python3.1
python3.2mu (--with-pymalloc --with-wide-unicode)
python3.3m (--with-pymalloc)

在默认 Python3 发行版中使用宽 unicode 标志会导致此错误吗?如果是这样,这是怎么回事?

为了清楚起见,ENOMEM 分配失败将出现在malloc() 的任何变体中,即使是像malloc(512) 这样简单的变体。

【问题讨论】:

  • 请注意,将char** 转换为void** 无效。指针可能有不同的大小。使用void *buffer = NULL;,以后如果需要字节访问可以分配给char*
  • 您是否尝试过打印sizealignment 看看它们是否有意义?也许它们被更改为代码其他地方某些 UB 的副作用。
  • 即使我硬编码这些参数并调用文字 posix_memalign(buffer, 512, 512) 它仍然无法在 2.7 下分配
  • Python 3.3 始终是 AFAIK 构建的,就好像您已经定义了 --with-wide-unicode
  • @filmor 如果我们查看古代历史是:docs.python.org/2.3/whatsnew/section-pymalloc.html 特别是In 2.3, pymalloc has had further enhancements and is now enabled by default 这表明 PyMalloc 应该内置到 2.7 和 3.1 中,但它们缺少 m 标签,这是不一致的还是那些没有它的版本是真正构建的?我机器上的 2.7 是 Ubuntu LTS 附带的版本。

标签: python c memory-management cpython


【解决方案1】:

为了快速解决问题,请坚持使用mmap 而不是malloc+memalign

【讨论】:

    【解决方案2】:

    posix_memalign 在一个编译环境中可能与另一个编译环境中的代码不同。您可以轻松想象 Python 3 将使用 different feature test 宏到 Python 2。这可能意味着它最终会运行不同的代码。

    您可能会查看使用的符号... ldd 或 nm 的输出通常会有错误的名称,表明实际使用的是哪个版本。

    此外,分配系统调用的 strace 显示了什么?我发现这是查看传入的参数是否不正确的好方法,这可能是获得 ENOMEM 的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-26
      • 1970-01-01
      • 2019-03-22
      • 2020-05-14
      • 2021-01-18
      • 1970-01-01
      • 2012-06-10
      相关资源
      最近更新 更多