메모리 매핑된 파일
메모리 매핑된 파일에는 가상 메모리에 있는 파일의 내용이 포함됩니다. 파일과 메모리 공간 사이의 이 매핑을 통해 여러 프로세스를 포함한 응용 프로그램에서 메모리를 직접 읽고 쓰는 방식으로 파일을 수정할 수 있습니다. .NET Framework 버전 4부터는 네이티브 Windows 함수가 메모리 매핑된 파일에 액세스할 때와 같은 방식으로 관리 코드를 사용하여 메모리 매핑된 파일에 액세스할 수 있습니다. 이에 대해서는 MSDN 라이브러리의 Managing Memory-Mapped Files in Win32에 설명되어 있습니다.
메모리 매핑된 파일에는 다음과 같은 두 가지 형식이 있습니다.
지속형 메모리 매핑된 파일
지속형 파일은 디스크의 소스 파일에 연결되어 메모리 매핑된 파일입니다. 마지막 프로세스에서 파일 작업을 마치고 나면 데이터가 디스크의 소스 파일에 저장됩니다. 이러한 형식의 메모리 매핑된 파일은 매우 큰 소스 파일을 사용하여 작업하는 데 적합합니다.
비지속형 메모리 매핑된 파일
비지속형 파일은 디스크의 소스 파일에 연결되지 않은 채 메모리 매핑된 파일입니다. 마지막 프로세스에서 파일 작업을 마치고 나면 데이터가 삭제되고 가비지 수집을 통해 파일이 회수됩니다. 이러한 파일은 IPC(프로세스 간 통신)용 공유 메모리를 만드는 데 적합합니다.
프로세스, 뷰 및 메모리 관리
메모리 매핑된 파일은 여러 프로세스에서 공유할 수 있습니다. 파일을 만든 프로세스를 통해 할당된 일반 이름을 사용하여 여러 프로세스에서 메모리 매핑된 파일 하나에 매핑할 수 있습니다.
메모리 매핑된 파일을 사용하여 작업하려면 메모리 매핑된 파일 전체 또는 일부의 뷰를 만들어야 합니다. 또한 메모리 매핑된 파일의 한 부분에 대한 뷰를 여러 개 만들어 동시 메모리를 만들 수도 있습니다. 두 개의 뷰를 동시에 유지하려면 두 뷰를 한 메모리 매핑된 파일에서 만들어야 합니다.
메모리 매핑된 파일이 메모리 매핑에 사용할 수 있는 응용 프로그램의 논리 메모리 공간 크기(32비트 컴퓨터의 경우 2GB)보다 클 경우 여러 개의 뷰를 사용해야 할 수도 있습니다.
뷰에는 스트림 액세스 뷰와 임의 액세스 뷰라는 두 가지 형식이 있습니다. 파일에 순차적으로 액세스하려면 스트림 액세스 뷰를 사용합니다. 이는 비지속형 파일과 IPC에 적합한 뷰입니다. 지속형 파일을 사용하여 작업하는 데는 임의 액세스 뷰가 더 적합합니다.
메모리 매핑된 파일에는 운영 체제의 메모리 관리자를 통해 액세스하므로 파일이 자동으로 여러 페이지로 분할되고 필요한 부분에 액세스하게 됩니다. 사용자가 직접 메모리를 관리할 필요가 없습니다.
다음 그림에서는 여러 프로세스가 동일한 메모리 매핑된 파일에 대한 뷰를 동시에 서로 겹치게 여러 개 가질 수 있는 방법을 보여 줍니다.
메모리 매핑된 파일의 서로 겹치는 여러 개의 뷰
메모리 매핑된 파일을 사용한 프로그래밍
다음 표에는 메모리 매핑된 파일 개체와 해당 멤버를 사용하기 위해 알아 둬야 할 지침이 나와 있습니다.
Task |
사용할 메서드 또는 속성 |
---|---|
디스크에 있는 파일에서 지속형 메모리 매핑된 파일을 나타내는 MemoryMappedFile 개체 가져오기 |
|
비지속형 메모리 매핑된 파일(디스크의 파일에 연결되지 않은 파일)을 나타내는 MemoryMappedFile 개체 가져오기 |
MemoryMappedFile.CreateNew 메서드 - 또는 - |
기존의 메모리 매핑된 파일(지속형 또는 비지속형)의 MemoryMappedFile 개체 가져오기 |
|
메모리 매핑된 파일에 순차적으로 액세스하는 뷰에 대한 UnmanagedMemoryStream 개체 가져오기 |
|
메모리 매핑된 파일에 무작위로 액세스하는 뷰에 대한 UnmanagedMemoryAccessor 개체 가져오기 |
|
비관리 코드에 사용할 SafeMemoryMappedViewHandle 개체 가져오기 |
MemoryMappedFile.SafeMemoryMappedFileHandle 속성. - 또는 - MemoryMappedViewAccessor.SafeMemoryMappedViewHandle 속성. - 또는 - |
뷰를 만들 때까지 메모리 할당 지연(비지속형 파일에만 해당) 현재 시스템 페이지 크기를 확인하려면 Environment.SystemPageSize 속성을 사용합니다. |
MemoryMappedFileOptions.DelayAllocatePages 값이 포함된 CreateNew 메서드 - 또는 - MemoryMappedFileOptions 열거형을 매개 변수로 갖는 CreateOrOpen 메서드 |
보안
메모리 매핑된 파일을 만들 때 MemoryMappedFileAccess 열거형을 매개 변수로 갖는 다음과 같은 메서드를 사용하여 액세스 권한을 적용할 수 있습니다.
MemoryMappedFileRights를 매개 변수로 갖는 OpenExisting 메서드를 사용하여 기존의 메모리 매핑된 파일을 여는 데 필요한 액세스 권한을 지정할 수 있습니다.
또한 미리 정의된 액세스 규칙이 들어 있는 MemoryMappedFileSecurity 개체를 포함할 수 있습니다.
메모리 매핑된 파일에 대한 액세스 규칙을 새로 만들거나 변경한 후 적용하려면 SetAccessControl 메서드를 사용합니다. 기존 파일에서 액세스 또는 감사 규칙을 검색하려면 GetAccessControl 메서드를 사용합니다.
예제
지속형 메모리 매핑된 파일
CreateFromFile 메서드는 디스크에 있는 기존 파일에서 메모리 매핑된 파일을 만듭니다.
다음 예제에서는 매우 큰 파일의 일부에 대한 메모리 매핑된 뷰를 만들고 그 일부를 조작합니다.
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);
}
}
}
다음 예제에서는 동일한 메모리 매핑된 파일을 다른 프로세스에 대해 엽니다.
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);
}
}
비지속형 메모리 매핑된 파일
CreateNew 및 CreateOrOpen 메서드는 디스크에 있는 기존 파일에 매핑되지 않는 메모리 매핑된 파일을 만듭니다.
다음 예제는 메모리 매핑된 파일에 부울 값을 쓰는 세 개의 서로 다른 프로세스(콘솔 응용 프로그램)로 구성되어 있습니다. 다음과 같은 순서로 작업이 수행됩니다.
Process A에서 메모리 매핑된 파일을 만들고 이 파일에 값을 씁니다.
Process B에서 메모리 매핑된 파일을 열고 이 파일에 값을 씁니다.
Process C에서 메모리 매핑된 파일을 열고 이 파일에 값을 씁니다.
Process A에서 메모리 매핑된 파일의 값을 읽고 표시합니다.
Process A에서 메모리 매핑된 파일을 닫으면 가비지 수집에 의해 해당 파일이 즉시 회수됩니다.
이 예제를 실행하려면 다음을 수행합니다.
응용 프로그램을 컴파일하고 세 개의 명령 프롬프트 창을 엽니다.
첫째 명령 프롬프트 창에서 Process A를 실행합니다.
두 번째 명령 프롬프트 창에서 Process B를 실행합니다.
Process A로 돌아가 Enter 키를 누릅니다.
세 번째 명령 프롬프트 창에서 Process C를 실행합니다.
Process A로 돌아가 Enter 키를 누릅니다.
Process A의 출력은 다음과 같습니다.
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
프로세스 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();
}
}
}
프로세스 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.");
}
}
}
프로세스 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.");
}
}
}