Windows:Different Process to shared memroy
===
**Shared Memory** - 共享記憶體,能允許不同程序去訪問相同的記憶體空間,那它實現了跨程序的溝通,也被稱為 Interprocess Communictaion(IPC)[Windows 各種 Interprocess Communications 方式](https://docs.microsoft.com/en-us/windows/win32/ipc/interprocess-communications?redirectedfrom=MSDN#using-a-file-mapping-for-ipc),Shared Memory 是 C/C++ 開發者常用的資料交換方式,MSDN上也有[範例](https://docs.microsoft.com/zh-tw/windows/win32/memory/creating-named-shared-memory?redirectedfrom=MSDN)可以參考。
而在工作上碰到多執行檔對單一DLL進行存取,那在存取資源只有一個的情況下,又必須讓多行程(Multi Process)同時執行,就得使用互斥鎖(Mutex)的機制來達成。
單行程:

>http://white5168.blogspot.com/2013/03/createmutex.html#.YJjjULUzaUk
多行程:每個行程都可以去使用單一個資源,不會有多個行程去使用一個資源的情況發生,那該行程使用完資源後會將該資源的使用權交由下個行程去使用。

>http://white5168.blogspot.com/2013/03/createmutex.html#.YJjjULUzaUk
## 應用
工作上遇到已經寫好的 python 應用與原有的 C code 該怎麼去做資料上的傳遞:
1. 把 py 應用改寫為 C
2. 把 C 的傳遞資料 library,透過 Swig 轉為 python module 讓 python 可以讀取
3. 將 python 程序的資料寫入到特定記憶體空間,再讓 C 程序去讀取該記憶體空間
當然上面這些方法都有想測試,由於 1 的方法很耗時間,2 的方法因為 C 寫成的 library 過於複雜,本人還不太會用 Swig 去轉譯,因此選了 3 進行。
上面都有提到 shared memory 的基本知識,那這邊就直接上 code 了,首先是 python 的傳值,透過 mmap 將要傳遞的資料映射到記憶體特定記憶體空間內,透過 ctypes 來宣告欲輸入的 structure 讓後續 C 好讀取與應用。
**Python:Write**
```python=
import _winapi
import mmap
import cv2
from ctypes import c_ulong, c_ubyte, Structure, POINTER, sizeof, memmove
class Data(Structure):
_fields_ = [("format", c_ulong),
("width", c_ulong),
("height", c_ulong),
("data", (c_ubyte * 2764800))] ## 2764800 為 1280 * 720 * 4
maxBufferSize = sizeof(Data)+ 4 * 1920 * 1080 ## 設 buffer 大小,可以自訂
temp_name = "testName"
# h_map = _winapi.CreateFileMapping(
# _winapi.INVALID_HANDLE_VALUE,
# _winapi.NULL,
# _winapi.PAGE_READWRITE,
# 0,
# size & 0xFFFFFFFF,
# temp_name
# )
mm = mmap.mmap(0, maxBufferSize, temp_name)
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
while True:
start = time.perf_counter()
ret, frame = cap.read()
# rgba = cv2.cvtColor(frame, cv2.COLOR_RGB2RGBA)
d = frame.ctypes.data_as(POINTER(c_ubyte))
# fourcc = cv2.VideoWriter_fourcc("R", "G", "B", "4")
img_buf = (c_ubyte * frame.nbytes)() ## 建立 buffer,讓影像放入
memmove(img_buf, d, frame.nbytes)
data = Data(fourcc, frame.shape[1], frame.shape[0], img_buf) ## 資料定義完畢
mm.seek(0)
mm.write(data)
mm.flush()
stop = time.perf_counter()
print("Writing Duration:", (stop - start) * 1000, "ms")
mm.close()
```
**Cpp:Read**
目前這只讀取一次資訊並顯示影像
```cpp=
#include <Windows.h>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>
using namespace std;
using namespace cv;
struct Frame {
DWORD format;
DWORD width;
DWORD height;
DWORD* data;
};
class IpcBridgePrivate
{
public:
std::wstring m_pipeName;
HANDLE m_fileHandle;
Frame* m_frame;
};
const int BUF_SIZE = sizeof(Frame)+ 4 * 1920 * 1080;
//const int BUF_SIZE = 256;
TCHAR szName[] = TEXT("Local\\AVerUSBVCam");
int main(void)
{
IpcBridgePrivate* d = new IpcBridgePrivate();
d->m_fileHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, szName);
if (NULL == d->m_fileHandle)
{
cout << "Could not open file mapping object :" << ::GetLastError() << endl;
return EXIT_FAILURE;
}
d->m_frame = reinterpret_cast<Frame*>(MapViewOfFile(d->m_fileHandle,
FILE_MAP_ALL_ACCESS,
0,
0,
BUF_SIZE));
if (NULL == d->m_frame)
{
cout << "Could not map view of file :" << GetLastError() << endl;
CloseHandle(d->m_fileHandle);
return EXIT_FAILURE;
}
BYTE *data = new BYTE[3686400];
CopyMemory(data, &d->m_frame->data, 3686400);
cout << d->m_frame->format << endl;
cout << d->m_frame->width << endl;
cout << d->m_frame->height << endl;
Mat frame = Mat(d->m_frame->height, d->m_frame->width, CV_8UC4, data);
imshow("frame", frame);
waitKey(0);
UnmapViewOfFile(d->m_fileHandle);
CloseHandle(d->m_fileHandle);
delete d;
return EXIT_SUCCESS;
}
}
```
## 問題
在過程中需要注意 32 bit 與 64 bit 問題,會因為不同位元導致寫入與讀取出來的數值有差異,上面範例 Frame 的型態宣告為 DWORD,32 / 64 位元它沒有改變大小,所以執行是可以寫/讀的。
>Sharing memory between 32-bit and 64-bit processes in Windows:https://stackoverflow.com/questions/36720383/sharing-memory-between-32-bit-and-64-bit-processes-in-windows
>Sharing memory between 32-bit and 64-bit processes in Windows:https://stackoverflow.com/questions/39419/how-large-is-a-dword-with-32-and-64-bit-code
## 參考
>共享記憶體機制:https://hackmd.io/@sysprog/linux-shared-memory
>關於 Shared Memory 的兩三事:https://blog.darkthread.net/blog/about-shared-memory/
>處理序間通訊:https://docs.microsoft.com/zh-tw/windows/win32/ipc/interprocess-communications?redirectedfrom=MSDN
>互斥鎖:https://zh.wikipedia.org/wiki/%E4%BA%92%E6%96%A5%E9%94%81