背景
在Unix-like系統進行IPC(Inter-process communication)通訊,Shared memory是效率最高的,我稱之為IPC的王中王。
簡介
本文講述在Windows Mobile和Windows Embedded CE下如何使用Shared Memory(共用記憶體)進行IPC(處理序間通訊)。示範如何使用Shared Memory共用資料,使用Named Event喚醒其他進程和使用Named Mutex去為共用資料加鎖。
主要IPC的方法
在Windows Mobile和Windows Embedded CE系統下,主要的IPC方法有以下幾種。
方法 |
通知 |
資料存放區 |
資料大小 |
Named events |
間接 |
N/A |
N/A |
Windows messages |
間接 |
在Message中 |
很小,只能傳輸Integer或者使用COPYDATASTRUCT 傳輸對象 |
Point-to-point message queues |
間接 |
在Message中 |
小型,存在boxing和unboxing問題 |
MSMQ |
直接 |
在Message中 |
小型,存在boxing和unboxing問題 |
TCP sockets |
直接 |
直接發送流(stream) |
中度 |
Memory mapped files |
N/A |
mapped file |
中度 |
Registry |
N/A |
註冊表 |
中度 |
File system |
N/A |
檔案 |
大型 |
Database |
N/A |
資料庫 |
大型 |
WCF |
N/A |
WCF訊息 |
中度 |
上述表格參考了Interprocess Communication with the .NET Compact Framework 1.0
關於上述的IPC的方法,沒有那個最好,選擇的時候需要根據具體需求來決定。這篇文章主要關注Named events和Shared Memory。 我之前也寫過關於其他IPC方法的文章,可以參考如下:
Windows Message
.NET Compact Framework下的處理序間通訊之Windows Message
MSMQ
WinCe和Windows Mobile下的MSMQ安裝
.NET Compact Framework下的處理序間通訊之MSMQ開發
Registry
.NET Compact Framework下註冊表匯出工具的開發
File System
Windows Mobile和Wince下使用TinyXML進行Native C++的開發
Database
.NET Compact Framework下SQL CE的使用 (實現了SqlCeHepler的封裝SqlCeHepler的測試類別,見.NET Compact Framework下的單元測試)
Windows Mobile下Native C++訪問SqlCe的封裝
SQL Server Express和SQL Server Compact的應用
.NET Campact Framework下SQL CE相容性問題
Windows Mobile下訪問Sqlite的Native C++封裝
如何壓縮SQLite的資料檔案
還有Point-to-point message queues, TCP sockets等等一部分主題沒有寫,如果有人希望我總結出來,請留言,我後續會補充進去。
Shared Memory的實現
實現的代碼主要參考了OpenNETCF的Smart Device Framework。
三個關鍵的類
MemoryMappedFile用於封裝共用記憶體,在Windows Embedded CE下的共用記憶體是一個Memory Mapped File,也就是一個記憶體對應檔,在所有進程的都可以訪問的記憶體中對應檔,操作該共用記憶體就類似於磁碟物理檔案。所以繼承於Stream,通過流來讀寫。
NamedMutex是進程層級的鎖,在Native C++一般使用CRITICAL_SECTION做鎖,而在.NET Compact Framework會使用monitor,微軟已經把monitor封裝到lock關鍵字中了,注意這個lock不是函數,是C#語言內嵌關鍵字。lock和Monitor等價。
關於lock和CRITICAL_SECTION的使用請參考下面文章。
Windows Mobile使用.NET Compact Framework開發多線程程式
Windows Mobile使用Native C++開發多線程程式
那麼,既然有了lock和CRITICAL_SECTION為什麼還需要Mutex呢,從效能來說Mutex的效率比Monitor也就是lock要低,所以我一般會使用lock而不是Mutex,但是lock不能支援跨進程加鎖,所以在這個case,我使用了Mutex。
.NET Compact Framework本身就提供了一個Mutex的類,可惜只是支援無名Mutex。Mutex分為命名Mutex和無名Mutex,無名的Mutex只能在同一個進程內部使用,不能跨進程使用,所以這裡封裝了個NamedMutex來支援命名Mutex,從而支援跨進程的鎖操作。
EventWaitHandle是通知Event。.NET Compact Framework本身封裝了AutoResetEvent和ManualResetEvent,但是他們都不支援跨進程,所以封裝了EventWaitHandle來實現跨進程的Event通知。
SharedMemoryWriter
SharedMemoryWriter負責往共用記憶體寫資料。
private void StartSharedMemoryWriting()
{
MemoryMappedFile mmf = MemoryMappedFile.CreateInMemoryMap("SharedMemoryBlock");
int i = 100;
while (started)
{
string s = "SharedMemory:" + i.ToString();
UpdateMessageList(s);
byte[] dataBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(s);
// Wait until it is safe to enter.
mutex.WaitOne();
try
{
mmf.Position = 0;
mmf.Write(dataBuffer, 0, dataBuffer.Length);
}
finally
{
// Release the Mutex.
mutex.ReleaseMutex();
}
// Raise the event
namedEvent.Set();
++i;
if (i > 999)
{
i = 100;
}
System.Threading.Thread.Sleep(500);
}
mmf.Close();
}
產生系統唯一命名的共用記憶體,在這個例子中使用了SharedMemoryBlock。每次寫共用記憶體的時候都通過Named Mutex對該記憶體加鎖。當寫完畢後通過Named Event通知SharedMemoryReader(讀共用記憶體)的進程。
SharedMemoryReader
SharedMemoryReader負責讀取共用記憶體的資料。
private void StartSharedMemoryReading()
{
MemoryMappedFile mmf = MemoryMappedFile.CreateInMemoryMap("SharedMemoryBlock");
byte[] dataBuffer = new byte[1024];
while (started)
{
if (namedEvent.WaitOne())
{
if (!started)
{
break;
}
namedEvent.Reset();
// Wait until it is safe to enter.
if (mutex.WaitOne())
{
try
{
mmf.Position = 0;
mmf.Read(dataBuffer, 0, 50);
}
finally
{
// Release the Mutex.
mutex.ReleaseMutex();
}
}
string s = System.Text.ASCIIEncoding.ASCII.GetString(dataBuffer, 0, 50);
UpdateMessageList(s);
}
}
mmf.Close();
}
開啟同樣名字(SharedMemoryBlock)的共用記憶體,這個進程會掛起直到收到Named Event的訊息,每次讀取的時候都需要使用Named Mutex來加鎖。
原始碼:http://files.cnblogs.com/procoder/SharedMemoryDemo.rar
環境: Windows Mobile 5 PPC + .NET Compact Framework 2.0