如何在Python中使用C malloc分配的内存(ctypes)

问题描述 投票:0回答:1

我猜下面的代码应该可以工作,但事实并非如此。有人可以向我解释一下如何使用这种指针吗? 当然,这只是示例代码。我需要解决的是使用来自外部库的指针(它有自己的分配器“library_allocete_memory”,内部使用 malloc)。我需要将数据从 python 代码复制到该内存中。

import ctypes
libc = ctypes.cdll.msvcrt
data = b'test data'
size = len(data)
ptr = libc.malloc(size)
ptr = ctypes.cast(ptr, ctypes.c_char_p)
# ptr = ctypes.create_string_buffer(size) # this is working, but I cannot allocate memory this way in my case
ctypes.memmove(ptr, data, size) # access violation is thrown here
python c ctypes
1个回答
0
投票

这里有两件事(可能)生成 Undefined Behavior:

  1. 根据[Python.Docs]:ctypes - 加载动态链接库强调是我的):

    注意:     通过

    cdll.msvcrt
    访问标准 C 库将使用该库的过时版本,该版本可能与 Python 使用的版本不兼容。如果可能,请使用本机 Python 功能,或者导入并使用 msvcrt 模块。

    还提到 [Python.Docs]:msvcrt - MS VC++ 运行时的有用例程

  2. [SO]:通过 ctypes 从 Python 调用的 C 函数返回不正确的值(@CristiFati 的答案)。这是使用 CTypes(调用函数)时的常见陷阱

这是一段有效的代码(在稍微不同的场景中)。

code00.py

#!/usr/bin/env python

import ctypes as cts
import sys
from ctypes.util import find_msvcrt


def main(*argv):
    data = b"test data"
    size = len(data)
    ptr = None
    use_msvcrt = bool(argv)  # If an argument (any value) is given
    if use_msvcrt:
        print(f"UCRT (found by CTypes): {find_msvcrt()}")
        size += 1  # + 1 for the NUL terminating char (whoever uses the buffer might rely on it)
        procexp = 0  # Enable this to inspect the (current) Python process (loaded .dlls) via Process Explorer tool
        if procexp:
            input("Press <Enter> to load UCRT...")
        libc = cts.cdll.msvcrt
        if procexp:
            input("UCRT loaded, press <Enter> move forward...")
        malloc = libc.malloc
        malloc.argtypes = (cts.c_size_t,)
        malloc.restype = cts.POINTER(cts.c_char)  # Rigorously should be cts.c_void_p, but choosing this form for convenience
        free = libc.free
        free.argtypes = (cts.c_void_p,)
        free.restype = None
        ptr = libc.malloc(size)
    else:
        ptr = cts.create_string_buffer(size)
    if ptr is None:
        print("Uninitialized pointer")
        return -1
    print(f"Ptr: {ptr} ({type(ptr)})")
    cts.memmove(ptr, data, size)
    if use_msvcrt:
        if (not ptr):
            print("NULL pointer")
            return -2
        print(f"Ptr data: {b''.join(ptr[i] for i in range(size - 1))}")
        free(ptr)
    else:
        print(f"Ptr data: {bytes(ptr)}")


if __name__ == "__main__":
    print(
        "Python {:s} {:03d}bit on {:s}\n".format(
            " ".join(elem.strip() for elem in sys.version.split("\n")),
            64 if sys.maxsize > 0x100000000 else 32,
            sys.platform,
        )
    )
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)

输出

[cfati@CFATI-5510-0:e:\Work\Dev\StackExchange\StackOverflow\q078205030]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32

Ptr: <ctypes.c_char_Array_9 object at 0x00000239067AB6C0> (<class 'ctypes.c_char_Array_9'>)
Ptr data: b'test data'

Done.


[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py dummyarg
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32

UCRT (found by CTypes): None
Ptr: <ctypes.LP_c_char object at 0x000002143B88B6C0> (<class 'ctypes.LP_c_char'>)
Ptr data: b'test data'

Done.

注释

  • 这通常不会照顾#1.。它在我的场景中有效,因为 mallocfree 是从同一个 UCRT 实例 (cts.cdll.msvcrt) 加载的。另外,(前面提到的)UCRT实例与主实例相同(由进程自动加载),但这只是巧合,不能放心依赖。 我没有使用
    WebUI,我不知道你是否可以向它传递空闲内存回调(这样传递当前 free 函数是安全的),或者甚至通知它不要释放指针并使用(推荐)cts.create_string_buffer方法

  • 检查一下可能也很有趣:

    • [SO]:如何将 bytearray 的一部分复制到 c_void_p 引用的内存,反之亦然? (@CristiFati 的回答)

    • [SO]:将 str 作为 int 数组传递给 Python C 扩展函数(使用 SWIG 扩展)(@CristiFati 的答案)

    • [SO]:如何在自定义嵌入式Python对象中存储指针(@CristiFati的答案)

    • [SO]:取消引用 c_void_p 的整个数据,而不仅仅是第一个字节(@CristiFati 的答案)

© www.soinside.com 2019 - 2024. All rights reserved.