[C#] MMF(Memory-Mapped File)로 IPC(프로세스간 통신, Inter-Process Communication)하기
카테고리: C# + Unity
IPC(프로세스간 통신, Inter-Process Communication)
IPC(Inter-Process Communication, 프로세스간 통신)는 여러 프로세스가 데이터를 주고받거나 작업을 조율하기 위해 사용하는 메커니즘을 의미한다. IPC는 운영 체제와 애플리케이션 개발에서 필수적인 요소로, 서로 독립적으로 실행되는 프로세스 간에 효율적이고 안전하게 통신할 수 있도록 지원한다.
IPC의 주요 기법
1. 파이프 (Pipes)
- 익명 파이프: 부모-자식 프로세스 간에 데이터 교환
- 이름 있는 파이프(Named Pipes): 서로 독립적인 프로세스 간 통신 가능
- 데이터가 일방향 또는 양방향으로 흐름
2. 메시지 큐 (Message Queues)
- 프로세스 간 메시지를 큐에 넣고 다른 프로세스가 이를 읽도록 하는 방식
- 비동기적 데이터 전송을 지원하며, 운영 체제에 의해 관리됨
3. 공유 메모리 (Shared Memory)
- 두 프로세스가 동일한 메모리 영역을 공유하여 데이터를 주고받는 방법
- 매우 빠르지만, 동기화 문제가 있을 수 있어 세마포어나 뮤텍스를 사용해 제어
4. 세마포어 (Semaphores)
- 동기화 메커니즘으로, 프로세스 간 자원 접근을 제어
- 공유 자원의 사용을 조율하기 위해 사용됩니다
5. 소켓 (Sockets)
- 네트워크를 통해 프로세스 간 데이터를 교환
- 동일한 시스템 내 또는 네트워크 상의 프로세스 간 통신에 적합
6. 시그널 (Signals)
- 프로세스 간 비동기적으로 메시지를 전달하는 간단한 방법
- 주로 이벤트 알림이나 인터럽트를 처리할 때 사용
7. 메모리 맵핑 (Memory-Mapped Files, MMF)
- 파일 내용을 메모리에 매핑하여, 여러 프로세스가 이를 공유하도록 지원
- 고속 데이터 교환이 필요한 경우 적합
8. 원격 프로시저 호출 (RPC, Remote Procedure Call)
- 한 프로세스가 다른 프로세스의 함수를 호출하듯 요청을 보내는 방식
- 네트워크 기반 분산 시스템에서 자주 사용됨
메모리 맵핑 (Memory-Mapped Files, MMF)
메모리 맵핑(Memory-Mapped Files, MMF)은 운영 체제가 파일의 내용을 메모리에 매핑하여, 파일 읽기와 쓰기 작업을 메모리 접근 방식으로 처리하는 기술이다. 이를 통해 프로세스는 파일의 내용을 디스크 I/O를 통해 직접 읽거나 쓰는 대신, 메모리에 매핑된 데이터를 활용하여 효율적으로 작업을 수행한다.
또한, 메모리 맵핑은 공유 메모리 공간을 통해 여러 프로세스가 동일한 메모리 영역에 접근할 수 있도록 한다. 이를 통해 프로세스 간에 데이터를 실시간으로 읽고 쓰는 것이 가능해져 빠르고 효율적인 통신이 이루어진다.
작동 방식
파일 매핑 생성
- 한 프로세스가 파일을 열고 mmap 시스템 호출 또는 운영 체제의 파일 매핑 API를 사용하여 해당 파일을 메모리에 매핑함
- 이를 통해 메모리 주소 공간에서 파일 내용을 처리함
공유 메모리 매핑
- 다른 프로세스도 동일한 파일을 메모리에 매핑하여 접근함
- 두 프로세스가 동일한 메모리 매핑을 공유하면, 한 프로세스가 데이터를 변경하면 다른 프로세스가 이를 즉시 확인 가능
동기화
- 메모리에 매핑된 데이터를 사용하는 경우 동기화가 중요
- 파일을 직접 변경하거나 매핑된 메모리를 사용할 때 동기화 메커니즘(예: 세마포어, 뮤텍스)을 사용하여 데이터 충돌 방지
활용 사례
- 멀티미디어 응용 프로그램: 영상 처리나 음향 데이터 스트리밍
- 데이터베이스 엔진: 고속 데이터 저장 및 읽기
- 운영 체제 커널: 로깅 시스템이나 디버깅 도구
- 게임 엔진: 대용량 리소스를 빠르게 로드
소스코드
- Producer.cs
using System;
using System.IO.MemoryMappedFiles;
using System.Text;
class Producer {
static void Main(string[] args) {
// Memory-mapped file 생성
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen("SharedMemory", 1024)) {
Console.WriteLine("Memory-mapped file 생성됨. 데이터를 작성합니다...");
// Memory-mapped file에 쓰기
using (var writer = mmf.CreateViewAccessor(0, 1024)) {
string message = "Hello from Producer!";
byte[] data = Encoding.UTF8.GetBytes(message);
// 데이터 쓰기
writer.Write(0, data.Length); // 데이터 길이 저장
writer.WriteArray(4, data, 0, data.Length); // 실제 데이터 저장
Console.WriteLine($"데이터 작성 완료: {message}");
}
}
Console.WriteLine("Enter 키를 눌러 종료합니다.");
Console.ReadLine();
}
}
- Consumer.cs
using System;
using System.IO.MemoryMappedFiles;
using System.Text;
class Consumer {
static void Main(string[] args) {
// Memory-mapped file 열기
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("SharedMemory")) {
Console.WriteLine("Memory-mapped file 열림. 데이터를 읽습니다...");
// Memory-mapped file 읽기
using (var reader = mmf.CreateViewAccessor(0, 1024)) {
int length = reader.ReadInt32(0); // 데이터 길이 읽기
byte[] data = new byte[length];
reader.ReadArray(4, data, 0, length); // 실제 데이터 읽기
string message = Encoding.UTF8.GetString(data);
Console.WriteLine($"데이터 읽음: {message}");
}
}
Console.WriteLine("Enter 키를 눌러 종료합니다.");
Console.ReadLine();
}
}
결과
Producer 실행:
Memory-mapped file 생성됨. 데이터를 작성합니다…
데이터 작성 완료: Hello from Producer!
Enter 키를 눌러 종료합니다.
Consumer 실행:
Memory-mapped file 열림. 데이터를 읽습니다…
데이터 읽음: Hello from Producer!
Enter 키를 눌러 종료합니다.