Archivos asignados a memoria
Un archivo asignado a memoria incluye el contenido de un archivo en la memoria virtual. Esta asignación entre un archivo y el espacio de memoria permite a una aplicación, incluidos varios procesos, modificar el archivo leyendo y escribiendo directamente en la memoria. A partir de .NET Framework versión 4, se puede utilizar el código administrado para tener acceso a los archivos asignados a memoria del mismo modo que las funciones Windows nativas tienen acceso a los archivos asignados a memoria, tal y como se describe en Managing Memory-Mapped Files in Win32 en MSDN Library.
Hay dos tipos de archivos asignados a memoria:
Archivos asignados a memoria persistentes
Los archivos persistentes son archivos asignados a memoria que están asociados a un archivo de origen ubicado en un disco. Cuando el último proceso termina de usar el archivo, los datos se guardan en el archivo de origen ubicado en el disco. Estos archivos asignados a memoria son idóneos para trabajar con archivos de origen muy grandes.
Archivos asignados a memoria no persistentes
Los archivos no persistentes son archivos asignados a memoria que no están asociados a un archivo ubicado en un disco. Cuando el último proceso termina de usar el archivo, se pierden los datos y la recolección de elementos no utilizados reclama el archivo. Estos archivos son idóneos para crear memoria compartida para las comunicaciones entre procesos (IPC).
Procesos, vistas y administración de memoria
Los archivos asignados a memoria se pueden compartir entre varios procesos. Los procesos pueden realizar una asignación al mismo archivo asignado a memoria usando un nombre común asignado por el proceso que creó el archivo.
Para trabajar con un archivo asignado a memoria, debe crear una vista de todo o parte del archivo asignado a memoria. También puede crear varias vistas a la misma parte del archivo asignado a memoria, creando de esta forma memoria simultánea. Para que dos vistas sigan siendo simultáneas, tienen que crearse a partir del mismo archivo asignado a memoria.
También puede ser necesario el uso de varias vistas si el archivo es mayor que el tamaño del espacio de memoria lógico de la aplicación disponible para la asignación de memoria (2 GB en un equipo de 32 bits).
Hay dos tipos de vista: vista de acceso secuencial y vista de acceso aleatorio. Use las vistas de acceso secuencial para obtener acceso secuencial a un archivo; es la vista recomendada para los archivos no persistentes y las IPC. Se recomiendan usar las vistas de acceso aleatorio para trabajar con archivos persistentes.
El acceso a los archivos asignados a memoria se realiza a través del administrador de memoria del sistema operativo, de modo que el archivo se divide automáticamente en varias páginas y el acceso se realizará según sea necesario. El usuario no tiene que encargarse de administrar la memoria.
En la siguiente ilustración se muestra cómo varios procesos pueden presentar simultáneamente varias vistas superpuestas del mismo archivo asignado a memoria.
Varias visas superpuestas de un archivo asignado a memoria
Programar con archivos asignados a memoria
En la siguiente tabla, se describe cómo usar los objetos de archivo asignado a memoria y sus miembros.
Tarea |
Métodos o propiedades que se usan |
---|---|
Para obtener un objeto MemoryMappedFile que representa un archivo asignado a memoria persistente de un archivo en disco. |
Método MemoryMappedFile.CreateFromFile. |
Para obtener un objeto MemoryMappedFile que representa un archivo asignado a memoria no persistente (no asociado a ningún archivo en disco). |
Método MemoryMappedFile.CreateNew. -O bien- Método MemoryMappedFile.CreateOrOpen. |
Para obtener un objeto MemoryMappedFile de un archivo asignado a memoria existente (persistente o no persistente). |
Método MemoryMappedFile.OpenExisting. |
Para obtener un objeto UnmanagedMemoryStream de una vista de acceso secuencial al archivo asignado a memoria. |
|
Para obtener un objeto UnmanagedMemoryAccessor de una vista de acceso aleatorio a un archivo asignado a memoria. |
|
Para obtener un objeto SafeMemoryMappedViewHandle que se va a usar con código no administrado. |
Propiedad MemoryMappedFile.SafeMemoryMappedFileHandle. -O bien- Propiedad MemoryMappedViewAccessor.SafeMemoryMappedViewHandle. -O bien- Propiedad MemoryMappedViewStream.SafeMemoryMappedViewHandle. |
Para retrasar la asignación de memoria hasta que se crea una vista (solo archivos no persistentes). (Para determinar el actual tamaño de página del sistema, utilice la propiedad Environment.SystemPageSize.) |
Método CreateNew con el valor MemoryMappedFileOptions.DelayAllocatePages. -O bien- Métodos CreateOrOpen con la enumeración MemoryMappedFileOptions como parámetro. |
Seguridad
Se pueden aplicar derechos de acceso cuando se crea un archivo asignado a memoria si se usan los siguientes métodos con la enumeración MemoryMappedFileAccess como parámetro:
Se pueden especificar derechos de acceso para abrir un archivo asignado a memoria existente si se usan los métodos OpenExisting con la enumeración MemoryMappedFileRights como parámetro.
Además, se puede incluir un objeto MemoryMappedFileSecurity, que contiene reglas de acceso predefinidas.
Para aplicar reglas de acceso nuevas o modificadas a un archivo asignado a memoria, utilice el método SetAccessControl. Para recuperar las reglas de acceso o de auditoría de un archivo existente, utilice el método GetAccessControl.
Ejemplos
Archivos asignados a memoria persistentes
Los métodos CreateFromFile crean un archivo asignado a memoria a partir de un archivo existente en disco.
En el ejemplo siguiente se crea una vista asignada a memoria de una parte de un archivo muy grande y se manipula una parte de él.
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Runtime.InteropServices
Class Program
Sub Main()
Dim offset As Long = &H10000000 ' 256 megabytes
Dim length As Long = &H20000000 ' 512 megabytes
' Create the memory-mapped file.
Using mmf = _
MemoryMappedFile.CreateFromFile("c:\ExtremelyLargeImage.data", _
FileMode.Open,"ImgA")
' Create a random access view, from the 256th megabyte (the offset)
' to the 768th megabyte (the offset plus length).
Using accessor = mmf.CreateViewAccessor(offset, length)
Dim colorSize As Integer = Marshal.SizeOf(GetType(MyColor))
Dim color As MyColor
Dim i As Long = 0
' Make changes to the view.
Do While (i < length)
accessor.Read(i, color)
color.Brighten(10)
accessor.Write(i, color)
Loop
End Using
End Using
End Sub
End Class
Public Structure MyColor
Public Red As Short
Public Green As Short
Public Blue As Short
Public Alpha As Short
' Make the view brighter.
Public Sub Brighten(ByVal value As Short)
Red = CType(Math.Min(Short.MaxValue, _
(CType(Red, Integer) + value)), Short)
Green = CType(Math.Min(Short.MaxValue, _
(CType(Green, Integer) + value)), Short)
Blue = CType(Math.Min(Short.MaxValue, _
(CType(Blue, Integer) + value)), Short)
Alpha = CType(Math.Min(Short.MaxValue, _
(CType(Alpha, Integer) + value)), Short)
End Sub
End Structure
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
long offset = 0x10000000; // 256 megabytes
long length = 0x20000000; // 512 megabytes
// Create the memory-mapped file.
using (var mmf =
MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data",
FileMode.Open,"ImgA"))
{
// Create a random access view, from the 256th megabyte (the offset)
// to the 768th megabyte (the offset plus length).
using (var accessor = mmf.CreateViewAccessor(offset, length))
{
int colorSize = Marshal.SizeOf(typeof(MyColor));
MyColor color;
// Make changes to the view.
for (long i = 0; i < length; i += colorSize)
{
accessor.Read(i, out color);
color.Brighten(10);
accessor.Write(i, ref color);
}
}
}
}
public struct MyColor
{
public short Red;
public short Green;
public short Blue;
public short Alpha;
// Make the view brigher.
public void Brighten(short value)
{
Red = (short)Math.Min(short.MaxValue, (int)Red + value);
Green = (short)Math.Min(short.MaxValue, (int)Green + value);
Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
}
}
}
En el siguiente ejemplo, se abre el mismo archivo asignado a memoria para otro proceso.
Imports System
Imports System.IO.MemoryMappedFiles
Imports System.Runtime.InteropServices
Class Program
Public Shared Sub Main(ByVal args As String())
' Assumes another process has created the memory-mapped file.
Using mmf = MemoryMappedFile.OpenExisting("ImgA")
Using accessor = mmf.CreateViewAccessor(4000000, 2000000)
Dim colorSize As Integer = Marshal.SizeOf(GetType(MyColor))
Dim color As MyColor
' Make changes to the view.
Dim i As Long = 0
While i < 1500000
accessor.Read(i, color)
color.Brighten(30)
accessor.Write(i, color)
i += colorSize
End While
End Using
End Using
End Sub
End Class
Public Structure MyColor
Public Red As Short
Public Green As Short
Public Blue As Short
Public Alpha As Short
' Make the view brigher.
Public Sub Brighten(ByVal value As Short)
Red = CShort(Math.Min(Short.MaxValue, CInt(Red) + value))
Green = CShort(Math.Min(Short.MaxValue, CInt(Green) + value))
Blue = CShort(Math.Min(Short.MaxValue, CInt(Blue) + value))
Alpha = CShort(Math.Min(Short.MaxValue, CInt(Alpha) + value))
End Sub
End Structure
using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
// Assumes another process has created the memory-mapped file.
using (var mmf = MemoryMappedFile.OpenExisting("ImgA"))
{
using (var accessor = mmf.CreateViewAccessor(4000000, 2000000))
{
int colorSize = Marshal.SizeOf(typeof(MyColor));
MyColor color;
// Make changes to the view.
for (long i = 0; i < 1500000; i += colorSize)
{
accessor.Read(i, out color);
color.Brighten(20);
accessor.Write(i, ref color);
}
}
}
}
}
public struct MyColor
{
public short Red;
public short Green;
public short Blue;
public short Alpha;
// Make the view brigher.
public void Brighten(short value)
{
Red = (short)Math.Min(short.MaxValue, (int)Red + value);
Green = (short)Math.Min(short.MaxValue, (int)Green + value);
Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
}
}
Archivos asignados a memoria no persistentes
Los métodos CreateOrOpen y CreateNew crean un archivo asignado a memoria que no está asignado a un archivo existente en el disco.
El ejemplo siguiente consta de tres procesos independientes (aplicaciones de consola) que escriben valores booleanos en un archivo asignado a memoria. Se produce la siguiente secuencia de acciones:
Process A crea el archivo asignado a memoria y escribe un valor en él.
Process B abre el archivo asignado a memoria y escribe un valor en él.
Process C abre el archivo asignado a memoria y escribe un valor en él.
Process A lee y muestra los valores del archivo asignado a memoria.
Una vez que Process A termina con el archivo asignado a memoria, la recolección de elementos no utilizados recupera el archivo inmediatamente.
Para ejecutar este ejemplo, haga lo siguiente:
Compile las aplicaciones y abra tres ventanas de símbolo del sistema.
En la primera ventana de símbolo del sistema, ejecute Process A.
En la segunda ventana de símbolo del sistema, ejecute Process B.
Vuelva a Process A y presione ENTRAR.
En la tercera ventana de símbolo del sistema, ejecute Process C.
Vuelva a Process A y presione ENTRAR.
La salida de Process A es la siguiente:
Start Process B and press ENTER to continue.
Start Process C and press ENTER to continue.
Process A says: True
Process B says: False
Process C says: True
Process A
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading
Module Module1
' Process A:
Sub Main()
Using mmf As MemoryMappedFile = MemoryMappedFile.CreateNew("testmap", 10000)
Dim mutexCreated As Boolean
Dim mTex As Mutex = New Mutex(True, "testmapmutex", mutexCreated)
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream()
Dim writer As BinaryWriter = New BinaryWriter(Stream)
writer.Write(1)
End Using
mTex.ReleaseMutex()
Console.WriteLine("Start Process B and press ENTER to continue.")
Console.ReadLine()
Console.WriteLine("Start Process C and press ENTER to continue.")
Console.ReadLine()
mTex.WaitOne()
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream()
Dim reader As BinaryReader = New BinaryReader(Stream)
Console.WriteLine("Process A says: {0}", reader.ReadBoolean())
Console.WriteLine("Process B says: {0}", reader.ReadBoolean())
Console.WriteLine("Process C says: {0}", reader.ReadBoolean())
End Using
mTex.ReleaseMutex()
End Using
End Sub
End Module
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;
class Program
{
// Process A:
static void Main(string[] args)
{
using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 10000))
{
bool mutexCreated;
Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated);
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(1);
}
mutex.ReleaseMutex();
Console.WriteLine("Start Process B and press ENTER to continue.");
Console.ReadLine();
Console.WriteLine("Start Process C and press ENTER to continue.");
Console.ReadLine();
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryReader reader = new BinaryReader(stream);
Console.WriteLine("Process A says: {0}", reader.ReadBoolean());
Console.WriteLine("Process B says: {0}", reader.ReadBoolean());
Console.WriteLine("Process C says: {0}", reader.ReadBoolean());
}
mutex.ReleaseMutex();
}
}
}
Process B
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading
Module Module1
' Process B:
Sub Main()
Try
Using mmf As MemoryMappedFile = MemoryMappedFile.OpenExisting("testmap")
Dim mTex As Mutex = Mutex.OpenExisting("testmapmutex")
mTex.WaitOne()
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream(1, 0)
Dim writer As BinaryWriter = New BinaryWriter(Stream)
writer.Write(0)
End Using
mTex.ReleaseMutex()
End Using
Catch noFile As FileNotFoundException
Console.WriteLine("Memory-mapped file does not exist. Run Process A first." & vbCrLf & noFile.Message)
End Try
End Sub
End Module
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;
class Program
{
// Process B:
static void Main(string[] args)
{
try
{
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream(1, 0))
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(0);
}
mutex.ReleaseMutex();
}
}
catch (FileNotFoundException)
{
Console.WriteLine("Memory-mapped file does not exist. Run Process A first.");
}
}
}
Process C
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading
Module Module1
' Process C:
Sub Main()
Try
Using mmf As MemoryMappedFile = MemoryMappedFile.OpenExisting("testmap")
Dim mTex As Mutex = Mutex.OpenExisting("testmapmutex")
mTex.WaitOne()
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream(2, 0)
Dim writer As BinaryWriter = New BinaryWriter(Stream)
writer.Write(1)
End Using
mTex.ReleaseMutex()
End Using
Catch noFile As FileNotFoundException
Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B." & vbCrLf & noFile.Message)
End Try
End Sub
End Module
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;
class Program
{
// Process C:
static void Main(string[] args)
{
try
{
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream(2, 0))
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(1);
}
mutex.ReleaseMutex();
}
}
catch (FileNotFoundException)
{
Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B.");
}
}
}