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)的機制來達成。 單行程: ![](https://i.imgur.com/I8y9vDP.png) >http://white5168.blogspot.com/2013/03/createmutex.html#.YJjjULUzaUk 多行程:每個行程都可以去使用單一個資源,不會有多個行程去使用一個資源的情況發生,那該行程使用完資源後會將該資源的使用權交由下個行程去使用。 ![](https://i.imgur.com/eJGCL0M.png) >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