本文主要记载了在 Windows 下如何通过调用 WinApi ,利用
共享内存
实现 进程间通信 (Inter Process Communication, IPC).
本文主要是通过 FileMapping 的方式实现进程间通信。目前已实现进程间共享字符串以及 opencv 的 Mat 对象 (以
uchar *
方式)。
两个 C++ 进程之间的通信
上面的文章只实现了字符串的进程间通信,根据实际需要,需要进程间传递图像对象,此处使用 opencv 的
Mat
对象来表示。由于
Mat
对象有一个
uchar* data
属性,指向 Mat 的实际数据。因此,这里通过传输
uchar* data
到共享内存,并在 Reader 中重建
Mat
对象 (C++)。
参考文章
Windows进程间通信–共享内存映射文件(FileMapping)
.
注意:
这里在传递时需要将图像的 长、宽、高 等参数一并传递,这样在 Reader 中可以根据读到的尺寸进行恢复。因为在 Reader 端一开始得到的是共享内存的首地址。C++ 中用三个
int
类型来表示
width
,
height
,
channel
。这三个
int
是写到表示数据的
uchar *
的最前面的。
1 |
// code snippet in img_reader.cpp |
C++ 和 python 进程之间的通信
C++ 作 Writer, python 作 Reader
参考文章:
Shared Memory Example (Python, ctypes, VC++)
。
python 中 使用
ctypes
来调用
win32 api
,方法与 C++ Reader 中的类似。区别在于获取共享内存的首地址后,python 中可以通过 slicing 的方式获取不同地址的值。最后得到 width, height, channel 以及数据,将数据转换成 ndarray 即可使用 opencv 显示。
FileMapping 原理
这里使用了 FileMapping 的方式进行 IPC。
Writer 使用的 API 有
CreateFile
CreateFileMapping
MapViewOfFile
MapViewOfFile
最后返回的是共享内存的指针。通过将数据
memcpy
到这里实现共享。
Reader 使用的 API 有
OpenFileMapping
MapViewOfFile
MapViewOfFile
最后返回的是共享内存的指针。可通过从这里读数据重建图像。
关于以上提及 API 的详细解释:
File Mapping 代码
1 |
// img_writer.cpp |
1 |
// img_reader.cpp |
1 |
# img_reader.py |
img_writer in C++ img_reader in C++ img_reader in python
不知为什么,python 版的 reader 用脚本执行正常,但是用 pyinstaller 打包成 exe 后就无法获取共享内存的指针,
MapViewOfFile
始终返回 0,正在解决这个问题。
除此之外,发现 python 版中在执行完
OpenFileMappingA
和
MapViewOfFile
后,分别调用
GetLastError
都应该返回 0 (0 表示没有错误)。C++ 版本的 reader 就是如此。但是
img_reader.py
用脚本方式执行却都返回 2,按理说返回 2 了就表示有错,应该无法正确读取共享内存数据,但是用脚本方式执行却可以读取图像数据。这一点很奇怪。而将
img_reader.py
转换成 exe 后,在
OpenFileMappingA
后调用
GetLastError
返回 2,
MapViewOfFile
后返回 6,也无法正确读取图像数据。