在Python(SWIG)中如何将mmap转换为ctypes void**?
解决Python中mmap转void**适配SWIG封装C库的问题
核心逻辑对应
你的C代码核心是:通过mmap获取内存指针char* image_buf,再取该指针的地址作为void**赋值给trans.frame_buffers。在Python中需要复刻这个"指针的指针"的构造逻辑。
问题1:将mmap转换为指针
有两种可靠方式:
方式1:直接调用libc的mmap获取指针(推荐)
直接调用系统libc的mmap函数,能直接拿到底层指针,避免Python mmap模块的额外封装。注意要严格匹配C函数的参数类型:
import ctypes import os import mmap # 加载libc并声明mmap的函数原型(必须,否则ctypes会自动转换参数导致错误) libc = ctypes.CDLL("libc.so.6") libc.mmap.argtypes = [ ctypes.c_void_p, # addr ctypes.c_size_t, # length ctypes.c_int, # prot ctypes.c_int, # flags ctypes.c_int, # fd ctypes.c_off_t # offset ] libc.mmap.restype = ctypes.c_void_p # 打开设备文件 axidma_fd = os.open("/dev/axidma", os.O_RDWR | os.O_EXCL) image_byte_size = 188 # 调用mmap获取void*指针 image_ptr = libc.mmap( 0, image_byte_size, mmap.PROT_READ | mmap.PROT_WRITE, # 注意用PROT_*常量,不是ACCESS_* mmap.MAP_SHARED, axidma_fd, 0 )
方式2:从Python标准库mmap对象提取指针
如果已经使用了Python的mmap模块,可通过ctypes提取底层指针:
import mmap import ctypes axidma_fd = os.open("/dev/axidma", os.O_RDWR | os.O_EXCL) image_byte_size = 188 # 创建Python mmap对象 image_buf_1 = mmap.mmap( axidma_fd, image_byte_size, mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE ) # 提取底层char*指针,再转换为void* char_ptr = ctypes.c_char_p.from_buffer(image_buf_1) image_ptr = ctypes.c_void_p(char_ptr.value)
问题2:构造void**类型
C中的(void**)&image_buf是取指针变量的地址,在Python中对应以下步骤:
- 把获取到的
image_ptr存入一个ctypes.c_void_p类型的变量(对应C里的void*变量) - 用
ctypes.pointer()获取该变量的地址,得到的就是POINTER(c_void_p)类型,完全匹配C的void**
代码实现:
# 步骤1:创建存储指针的变量 frame_buf_ptr = ctypes.c_void_p(image_ptr) # 步骤2:构造void**并赋值给trans.frame_buffers trans.frame_buffers = ctypes.pointer(frame_buf_ptr)
效率对比:直接调用libc vs Python mmap
- 直接调用libc.mmap效率略高:少了Python mmap模块的封装层,但性能差异极小,普通场景可忽略。
- Python mmap模块优势:提供字节串式的操作接口,自动处理部分内存管理细节,更符合Python习惯。
- 若仅为给SWIG封装的C库提供指针,直接调用libc.mmap更直接,避免额外转换步骤。
解决之前的错误问题
c_void_p.from_buffer(mmap对象)得到None:from_buffer是读取缓冲区内容而非获取指针地址,正确方式是用from_buffer获取指向缓冲区的指针,再取其value属性。- libc.mmap指针赋值报错:直接传入
c_void_p是void*类型,而接口需要void**,必须先把指针存入变量再取其地址。 - byref/from_buffer类型不匹配:
byref是临时引用对象,不是正式的POINTER类型;from_buffer得到的是void*,需要再封装一层指针的指针才能匹配void**。
内容的提问来源于stack exchange,提问作者jcc18
相关产品推荐
相关产品推荐

