Memory-Mapped Files oder Speicherabbilddateien, wie sie so schön auf Deutsch heißen, bilden Inhalte von Dateien im virtuellen Speicher ab. Dadurch können Änderungen direkt im Speicher vorgenommen werden. Nicht nur von von der Applikation selbst, sondern auch von mehreren Prozessen. Dieser Artikel zeigt die Möglichkeiten mit .NET 4+.
Typen von Memory-mapped Files
Insgesamt stehen zwei unterschiedliche Typen zur Verfügung.
Persistierte Memory-mapped Files
Hier besteht ein Mapping zwischen einer Datei auf der Festplatte und dem Speicher. Änderungen können von mehreren Prozessen im Speicher vorgenommen werden. Wenn der letzte Prozess beendet wird, werden die Änderungen auf die Festplatte geschrieben und der Speicher wieder freigegeben. Diese Variante ist gerade für den Zugriff auf sehr große Dateien hilfreich und sinnvoll.
Nicht-persistierte Memory-mapped Files
Hier wird Speicher reserviert, der aber nicht mit einer existierenden Datei verlinkt ist. Dies wird gerne als Shared Memory für die Kommunikation zwischen mehreren Prozessen verwendet. Wenn der letzte zugreifende Prozess beendet wird, wird der Speicher wieder freigegeben.
Beispiel
Das Beispiel besteht aus zwei Konsolen-Anwendungen. Das erste erstellt einen Shared Memory und schreibt dort einen Integer-Wert hinein. Zusätzlich wird der Wert über einen Timer jede Sekunde abgegriffen und ausgegeben. Damit kann überprüft werde, ob sich der Wert verändert hat. MemoryMappedFile
stellt die notwendigen Methoden zur Anlage des Speicherabbildes zur Verfügung. Via CreateViewAccessor
eine View auf das gemappte File erstellt. Darauf kann im Anschluss zugegriffen werden. Via CreateViewStream
kann wird ein sequentieller Zugriff ermöglicht.
class Program
{
MemoryMappedFile mappedFile;
Timer timer = new Timer();
long offset = 0x00000000;
long length = 0x00000010;
static void Main(string[] args)
{
Program p = new Program();
p.WriteData();
p.Listen();
Console.ReadLine();
}
private void Listen()
{
timer.Interval = 1000;
timer.Elapsed += Timer_Elapsed;
timer.Enabled = true;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
using (var accessor = mappedFile.CreateViewAccessor())
{
var value = accessor.ReadInt32(0);
Console.WriteLine("Found value: " + value);
}
}
private void WriteData()
{
mappedFile = MemoryMappedFile.CreateOrOpen("MappingTest", length, MemoryMappedFileAccess.ReadWrite);
using (var accessor = mappedFile.CreateViewAccessor(offset, length))
{
int value = 27;
accessor.Write(0, value);
}
}
}
Die zweite Anwendung holt sich nun via MemoryMappedFile.OpenExisting
den Shared Memory und schreibt einen anderen Wert hinein:
class Program
{
static void Main(string[] args)
{
using (var mappedFile = MemoryMappedFile.OpenExisting("MappingTest"))
{
using (var accessor = mappedFile.CreateViewAccessor())
{
var value = 29;
accessor.Write(0, value);
}
}
}
}
Sobald das Timer-Intervall wieder vergangen ist, sollte der erste Prozess den neuen Wert erhalten.
Et voilá.