Shared Memory
摘要
因工作需求用上了「Shared Memory」這個方法,因此特別整理一篇文章備忘,包含了Shard Memory運作原理,各語言的實作範例(C#, C++, Boost C++),以及如何在UE4中應用。
何謂Shared Memory (截錄自黑暗執行緒)
跨 Process溝通有個術語,Interprocess Communictaion(IPC),在 Windows 平台有以下選擇:參考
Shared Memory是C/C++開發者常用的資料交換方式,故C/C++開發者在Windows平台也常選擇它做為溝通管道。
AccessChk工具 (下載)
可透過AccessChk工具來檢視Windows目前已開啟的MemoryMappedFile。SystemInternals有個AccessChk工具能列出Windows 所有可存取的檔案、資料夾、Registry、物件以及Windows服務。而MemoryMappedFile屬於一種Windows物件。
在其中尋找MemoryMappedFile名稱,若存在可看到類似以下記錄:
\Sessions\1\BaseNamedObjects\[MemoryMappedFileName]
Type: Section
Medium Mandatory Level (Default) [No-Write-Up]
RW NT AUTHORITY\SYSTEM
SECTION_ALL_ACCESS
RW DOMAIN\UserName
SECTION_ALL_ACCESS
RW DOMAIN\UserName-S-1-5-5-0-954410
SECTION_ALL_ACCESS
各語言的Shared Memory實作範例
如何在UE4中應用Shared Memory
雖然UE4 API有提供Shared Memory方法(MapNamedSharedMemoryRegion),不過在UE4論壇裡有失敗的例子(Creating shared memory on Windows always fails),我自己嘗試在UE4.18中使用UE4 API也是仍無法正常運作。
最後是參考jgcoded作法,使用Native C++在UE4中實作Windows shared memory來解決。在本文的範例中,分為一支只寫入數值的程式 (C#, using System.IO.MemoryMappedFiles),負責建立MemoryMappedFile,寫入9個double數值,以及一支只接收數值的程式 (UE4, Native C++, include <windows.h>),負責開啟MemoryMappedFile,讀取9個double數值。
Implement shared memory from C# to UE4 (C#)
Implement shared memory from C# to UE4 (UE4)
因工作需求用上了「Shared Memory」這個方法,因此特別整理一篇文章備忘,包含了Shard Memory運作原理,各語言的實作範例(C#, C++, Boost C++),以及如何在UE4中應用。
何謂Shared Memory (截錄自黑暗執行緒)
跨 Process溝通有個術語,Interprocess Communictaion(IPC),在 Windows 平台有以下選擇:參考
- Clipboard
程式 A 將内容貼進剪貼簿,程式 B 自剪貼簿取出内容。 - COM
OLE複合文件(Compound Document)讓Word文件可以內嵌Excel工作表,點兩下還能叫出 Excel進行編輯, OLE的基礎為 COM元件技術。 - Data Copy
程序 A向程式 B依約定的格式内容傳送WM_COPYDATA訊息 - DDE
DDE是一種允許不同應用程式交換不同格式資料的通訊協定,可視為剪貼簿的沿伸,除了一次性抛轉,還能持續傳輸資料。(效能相對差,已不建議使用) - File Mapping
File Mapping意指將檔案模擬成 Process中的一塊記憶體,當多個應用程式間透過共用 File Mapping交換資料,稱之為Named Shared Memory,在各種IPC方法中效能最佳,但必須透過Mutex等同步機制防止讀寫衝突。 - Mailslots
單向溝通,Mailslot Client送訊息給Mailslot Server,訊息在Server讀取後删除,支援跨機器傳送,還可一對多廣播。(廣播訊息長度限制 400bytes,一對一傳輸時訊息長度則由 Mailslot Server建立時決定) - Pipes
雙向傳輸,分為Anonymous Pipe及Named Pipe。Anonymous Pipe一般用於父程序與子程序間的標準輸入/輸出導向,雙向溝通要建兩條Pipe,不能跨網路且限於有從屬關係的 Process;Named Pipe則可用於任意Process間交換資料,並支援跨網路Process間傳輸。 - RPC
Remote Procedure Call(RPC)允許應用程式呼叫其他應用程式提供的函式功能,並可跨網路呼叫。Windows RPC符合 ISO DCE標準,支援跨作業系統系統整合。 - Windows Sockets
基於 TCP/IP 或其他網路協定制訂的抽象通訊介面,底層透過網路連線進行資料交換。
Shared Memory是C/C++開發者常用的資料交換方式,故C/C++開發者在Windows平台也常選擇它做為溝通管道。
AccessChk工具 (下載)
可透過AccessChk工具來檢視Windows目前已開啟的MemoryMappedFile。SystemInternals有個AccessChk工具能列出Windows 所有可存取的檔案、資料夾、Registry、物件以及Windows服務。而MemoryMappedFile屬於一種Windows物件。
- 下載AccessChk工具
- 於同一目錄建立批次檔accesschk_createObjList.bat,此指令能夠列出所有物件並存檔
- 以系統管理員身份執行accesschk_createObjList.bat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cd /d "%~DP0" | |
accesschk.exe -osv > objList.txt |
在其中尋找MemoryMappedFile名稱,若存在可看到類似以下記錄:
\Sessions\1\BaseNamedObjects\[MemoryMappedFileName]
Type: Section
Medium Mandatory Level (Default) [No-Write-Up]
RW NT AUTHORITY\SYSTEM
SECTION_ALL_ACCESS
RW DOMAIN\UserName
SECTION_ALL_ACCESS
RW DOMAIN\UserName-S-1-5-5-0-954410
SECTION_ALL_ACCESS
各語言的Shared Memory實作範例
如何在UE4中應用Shared Memory
雖然UE4 API有提供Shared Memory方法(MapNamedSharedMemoryRegion),不過在UE4論壇裡有失敗的例子(Creating shared memory on Windows always fails),我自己嘗試在UE4.18中使用UE4 API也是仍無法正常運作。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// DevelopmentEditor, Win64 | |
FGenericPlatformMemory::FSharedMemoryRegion* smr; | |
smr = FGenericPlatformMemory::MapNamedSharedMemoryRegion( | |
TEXT("SharedMemoryRegionName"), | |
true, | |
FGenericPlatformMemory::ESharedMemoryAccess::Write, | |
72); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LogHAL: Error: FGenericPlatformMemory::MapNamedSharedMemoryRegion not implemented on this platform |
最後是參考jgcoded作法,使用Native C++在UE4中實作Windows shared memory來解決。在本文的範例中,分為一支只寫入數值的程式 (C#, using System.IO.MemoryMappedFiles),負責建立MemoryMappedFile,寫入9個double數值,以及一支只接收數值的程式 (UE4, Native C++, include <windows.h>),負責開啟MemoryMappedFile,讀取9個double數值。
Implement shared memory from C# to UE4 (C#)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Data; | |
using System.Drawing; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using System.Windows.Forms; | |
namespace SharedMemoeyTest | |
{ | |
public partial class Form1 : Form | |
{ | |
public Form1() | |
{ | |
InitializeComponent(); | |
} | |
private void Form1_Load(object sender, EventArgs e) | |
{ | |
} | |
private void button1_Click(object sender, EventArgs e) | |
{ | |
Program.CreateMappedMemory(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using System.Windows.Forms; | |
// Shared memory required | |
using System.IO; | |
using System.IO.MemoryMappedFiles; | |
using System.Threading; | |
using System.Text; | |
namespace SharedMemoeyTest | |
{ | |
static class Program | |
{ | |
[STAThread] | |
static void Main() | |
{ | |
Application.EnableVisualStyles(); | |
Application.SetCompatibleTextRenderingDefault(false); | |
Application.Run(new Form1()); | |
} | |
public static void CreateMappedMemory() | |
{ | |
MemoryMappedFile mmf = MemoryMappedFile.CreateNew("SharedMemoeyTest", 72); | |
bool mutexCreated; | |
Mutex mutex = new Mutex(true, "SharedMemoeyTestMutex", out mutexCreated); | |
using (var stream = mmf.CreateViewStream()) | |
{ | |
using (BinaryWriter bw = new BinaryWriter(stream)) | |
{ | |
bw.Write(1d); | |
bw.Write(2d); | |
bw.Write(3d); | |
bw.Write(4d); | |
bw.Write(5d); | |
bw.Write(6d); | |
bw.Write(7d); | |
bw.Write(8d); | |
bw.Write(9d); | |
} | |
} | |
mutex.ReleaseMutex(); | |
} | |
} | |
} |
Implement shared memory from C# to UE4 (UE4)