在 Python 中将 mmap 转换为 ctypes void ** (SWIG)



我使用 swig 在 C 语言中生成了一个库的包装器。

我需要在 python 中复制 C 代码,但我不知道如何从 mmap 到 void **

在 C 中:

char *image_buf;
...
axidma_fd = open("/dev/axidma", O_RDWR|O_EXCL);
...
image_buf = mmap(NULL, image_byte_size, PROT_READ|PROT_WRITE,
MAP_SHARED, axidma_fd, 0);
...
trans.num_frame_buffers = 1;
trans.frame_buffers = (void **)&image_buf;

在蟒蛇中

axidma_fd = os.open("/dev/axidma", os.O_RDWR|os.O_EXCL)
...
image_buf_1 = mmap.mmap(vdma_fd,188, mmap.MAP_SHARED,mmap.ACCESS_WRITE| mmap.ACCESS_READ);
...
# Convert mmap to c_char 
char_pointer = c_char.from_buffer(image_buf_1) ???? (but type is c_char not pointer)
# Double void
trans.frame_buffers = ??

如何将 mmap 转换为指针?

如何获得双指针?

直接在 python 中执行此操作还是通过实例化 libc 库来执行此操作更有效? 比如这样

libc = ctypes.CDLL("libc.so.6")
image_mmap = ctypes.c_void_p(libc.mmap(0,188,mmap.ACCESS_WRITE| mmap.ACCESS_READ,mmap.MAP_SHARED,vdma_fd)

非常感谢

编辑 1它解释了我有点糟糕

如果我这样做:

image_buf_1 = mmap.mmap(vdma_fd,188, mmap.MAP_SHARED,mmap.ACCESS_WRITE| mmap.ACCESS_READ)
image_map = c_void_p.from_buffer(image_buf_1)

image_map为无

>>> image_map
c_void_p(None)

另一方面,如果我这样做

libc = ctypes.CDLL("libc.so.6")
image_mmap = ctypes.c_void_p(libc.mmap(0,188,mmap.ACCESS_WRITE| mmap.ACCESS_READ,mmap.MAP_SHARED,vdma_fd))

image_mmap是一个有效的指针。

>>> image_mmap
c_void_p(18446744073709551615)

但是当尝试将其分配给

trans.frame_buffers = image_mmap

报告:

trans.frame_buffers=image_mmap
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: in method 'axidma_video_transaction_frame_buffers_set', argument 2 of type 'void **'

我需要将 mmap 转换为无效**

已编辑 2

不工作:(

我已经定义了:

class struct_axidma_video_transaction(Structure):
pass
struct_axidma_video_transaction.__slots__ = [
'channel_id',
'num_frame_buffers',
'frame_buffers',
'frame',
]
struct_axidma_video_transaction._fields_ = [
('channel_id', c_int),
('num_frame_buffers', c_int),
('frame_buffers', POINTER(POINTER(None))),
('frame', struct_axidma_video_frame),
]

我需要将 mmap 转换为指针(指针将其传递给 frame_buffers 参数:

>>> from ctypes import c_int, c_void_p, byref, CDLL, POINTER,c_int64
>>> import mmap
>>> vdma_fd = os.open("/dev/axidma",os.O_RDWR|os.O_EXCL)
>>> type(vdma_fd)
<class 'int'>
>>> vdma_buf    = mmap.mmap (vdma_fd,188,mmap.MAP_SHARED, mmap.ACCESS_READ | mmap.ACCESS_WRITE)
>>> type(vdma_buf)
<class 'mmap.mmap'>
>>> image_buf_p = c_void_p.from_buffer(vdma_buf)
>>> type(image_buf_p)
<class 'ctypes.c_void_p'>
>>> image_buf_pp = byref(image_buf_p)
>>> type(image_buf_pp)
<class 'CArgObject'>
>>> trans.frame_buffers = image_buf_pp
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected LP_c_void_p instance, got CArgObject

如果我定义:

>>> image_buf_pp = c_void_p.from_buffer(image_buf_p)
>>> type(image_buf_pp)
<class 'ctypes.c_void_p'>
>>> trans.frame_buffers = image_buf_pp
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_void_p instance instead of LP_c_void_p instance
>>>

部分感谢这个答案:ctypes 引用双指针

试试ctypes.byref().示例代码:

import ctypes
import mmap
import os
FILENAME=#<your filename>
FILELENGTH=#<your file's length>
fd = os.open(FILENAME, os.O_RDWR|os.O_EXCL)
image_buf_1 = mmap.mmap(fd, FILELENGTH, mmap.MAP_SHARED|mmap.ACCESS_WRITE|mmap.ACCESS_READ)
image_map = ctypes.c_void_p.from_buffer(image_buf_1)
print(f"{image_map.value:x}")
print(f"{ctypes.byref(image_map)}")      # this is the pointer you're looking for
os.system(f'cat /proc/$PPID/maps | grep {FILENAME}')    # verify the pointer value
os.close(fd)

抱歉,我不知道你所说的"双指针"到底是什么意思;mmap 返回一个 void*,这里用 ctypes.byref(image_map) 表示。

至于什么更有效,你当然可以配置文件,但通常你的投资来自做 I/O 而不是设置 I/O(我假设 ctypes.DLL是通过 dlopen() 实现的,这对于已经加载的库来说是非常轻量级的)。除非你打开了大量的文件,否则我猜你想根据你是否要使用Python中的数据(你可能需要Python mmap对象)来选择你的方法。

--注意:我简要尝试了您引用的libc方法,但mmap返回了-1,参数无效。我没有调查我在那里做错了什么。

列出 [Python.Docs]: mmap - 内存映射文件支持。

检查 [SO]:使用CTypes时,通过 ctypes 从 Python 调用的 C 函数返回不正确的值(@CristiFati 的答案)。

CTypes中使用双(或多个)指针(像数据一样存储数组 - 就像图像一样)有点棘手(如[SO]:C++和Python:传递并返回从python到c ++的2D双指针数组(@CristiFati的答案))。
下面是一个使用数组的示例。

code00.py

#!/usr/bin/env python
import ctypes as ct
import mmap
import os
import sys

#CharPtrPtr = ct.POINTER(ct.POINTER(ct.c_char))

def main(*argv):
h = 3
w = 4
fname = "file.txt"
with open(fname, mode="wb") as f:
f.write(b"x00" * h * w)
fd = os.open(fname, os.O_RDWR | os.O_CREAT)
buf = mmap.mmap(fd, h * w, mmap.MAP_SHARED, mmap.ACCESS_WRITE | mmap.ACCESS_READ)
CharArrArr = ct.c_char * w * h
img = CharArrArr.from_buffer(buf)
for i in range(h):
for j in range(w):
img[i][j] = 0x41 + 2 * i * w + j
del img
buf.close()
os.close(fd)
with open(fname) as f:
print("File contents:", f.read())

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.")
sys.exit(rc)

输出

(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q073614606]> python ./code00.py
Python 3.8.10 (default, Jun 22 2022, 20:18:18) [GCC 9.4.0] 064bit on linux
File contents: ABCDIJKLQRST
Done.

看编辑,似乎我做得不对。为什么需要void**指针?你用它做什么(什么是跨性别)?请分享代码,而不仅仅是分散的片段。
如果我做对了,你需要获取mmap的返回值(作为 void*),并进一步传递它的地址(这是一个void**)。你可以这样做(使用代码片段):

image_buf_1 = mmap.mmap(vdma_fd, 188, mmap.MAP_SHARED, mmap.ACCESS_WRITE | mmap.ACCESS_READ)
image_map = ctypes.c_void_p.from_buffer(image_buf_1)
trans.frame_buffers = ctypes.pointer(image_mmap)

另一个问题(指针):您正在将fd0(即StdIn)传递给 libc.mmap(还要检查 2ndURL是否存在隐藏的错误),而对于 mmap.mmap,您使用的是"真实"描述符,所以我假设对于该描述符,内存无法正确映射(您应该在这两种情况下检查mmap结果 (image_buf_1))。

最新更新