Mapping a file into memory
- File을 프로세스의 가상 메모리 공간으로 매핑
- File I/O system call 사용하지 않고 접근
#include <sys/mman.h>
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
- addr : 매핑할 메모리 주소 hint(일반적으로 NULL사용)
- length : 매핑할 길이(byte단위)
- prot : memory protection mode
- PROT_NONE : 접근할 수 없음
- PROT_READ
- PROT_WRITE
- PROT_EXEC
- flags : 매핑 형태와 동작 방식 지정
- MAP_FIXED : Mapping 할 주소를 지정
- MAP_PRIVATE : 변경된 내용이 공유 및 반영되지 않음(사본에 작업)
- MAP_SHARED : Mapping도니 내용이 공유
- fd : 매핑할 파일의 file descriptor
- offset : 매핑할 시작 지점을 지정하는 offset(page size의 배수여야 함)
- return : 할당된 메모리 주소, fail : MAP_FAILED
- signal
- SIGBUS : mapping region is invalid
- SIGSEGV : try to write to a read-only mapping region
- mapping은 page 단위로 이루어짐
- page size = sysconf(_SC_PAGESIZE) or getpagesize()
- 파일의 크기는 사용하려는 메모리 영역보다 커야 함
- Copy-on-Write(CoW) : private mode로 write시, 복사본 생성을 미룸, 불필요한 복사를 피해서 부하를 줄임
removing a mapping
#include <sys/mman.h>
int munmap(void* addr, size_t length);
- addr : mapping 시작 주소
- length : mapping 길이
- return
Example
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CHECK_MMAP_SUCCESS(_addr) \
if (_addr == MAP_FAILED) { \
perror("mmap"); \
exit(1); \
}
#define printAddrs(msg) \
{ \
printf("%s\n", msg); \
printf("addr1 = %s", addr1); \
printf("addr2 = %s", addr2); \
}
int main(int argc, char* argv[]) {
int fd;
caddr_t addr1, addr2;
char fileName[255] = "input.dat";
if (argc > 1) strcpy(fileName, argv[1]);
if ((fd = open(fileName, O_RDWR)) == -1) {
perror("open");
exit(1);
}
int pageSize = getpagesize();
addr1 = mmap(NULL, pageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0);
CHECK_MMAP_SUCCESS(addr1);
addr2 = mmap(NULL, pageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, (off_t)0);
CHECK_MMAP_SUCCESS(addr2);
close(fd);
printf("%s", addr1);
addr1[0] = '1'; printAddrs("After addr1[0]=1");
addr2[0] = '2'; printAddrs("After addr2[0]=2"); // CoW
addr1[0] = '3'; printAddrs("After addr1[0]=3");
execlp("cat", "cat", fileName, NULL);
return 0;
}
synchronizing a file with a mapping
#include <sys/mman.h>
int msync(void* addr, size_t length, int flags);
- 강제로 page write-back 수행
- memory의 내용과 disk에 있는 파일 내용 동기화
- fflush()와 유사
- flags
- MS_SYNC : write-back이 끝날 때까지 대기
- MS_ASYNC : write-back을 비동기적으로 수행(기다리지 않고 바로 return)
- MS_INVALIDATE : 메모리에 변경된 내용 무효화
changing file size
#include <unistd.h>
#include <sys/types.h>
int truncate(const char* path, off_t length);
int ftruncate(int fd, off_t length);
- 파일의 크기를 변경(늘어난 부분은 0으로 초기화)
- 파일에 대한 쓰기 권한 필요
IPC with memory mapping
- 같은 memory mapping region을 공유함으로써 프로세스들 사이의 통신(대화) 가능
- 하지만, 프로세스들 간의 동기화를 잘 고려해서 사용해야 함
- IPC
- Pipe
- Message Queue
- Shared Meomory
- Semaphore
- Mutex
Example
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CHECK_MMAP_SUCCESS(_addr) \
if (_addr == MAP_FAILED) { \
perror("mmap"); \
exit(1); \
}
int main(int argc, char* argv[]) {
int fd;
caddr_t addr;
char fileName[255] = "input.dat";
if (argc > 1) strcpy(fileName, argv[1]);
if ((fd = open(fileName, O_RDWR)) == -1) {
perror("open");
exit(1);
}
int pageSize = getpagesize();
addr = mmap(NULL, pageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0);
CHECK_MMAP_SUCCESS(addr);
close(fd);
int pid = 0;
switch (pid = fork()) {
case -1:
perror("fork");
exit(1);
break;
case 0:
printf("1. Child Process : addr=%s", addr);
sleep(1);
addr[0] = 'x';
printf("2. Child Process : addr=%s", addr);
sleep(2);
printf("3. Child Process : addr=%s", addr);
break;
default:
printf("1. Parent Process : addr=%s", addr);
sleep(2);
printf("2. Parent Process : addr=%s", addr);
addr[1] = 'y';
printf("3. Parent Process : addr=%s", addr);
break;
}
return 0;
}
producer
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CHECK_MMAP_SUCCESS(_addr) \
if (_addr == MAP_FAILED) { \
perror("mmap"); \
exit(1); \
}
int main(int argc, char* argv[]) {
int fd;
char* addr;
char fileName[255] = "map.dat";
if (argc > 1) strcpy(fileName, argv[1]);
if ((fd = open(fileName, O_RDWR)) == -1) { perror("open"); exit(1); }
addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0);
CHECK_MMAP_SUCCESS(addr);
close(fd);
char c = 'a';
while (1) {
sleep(2);
addr[0] = c++;
if (c > 'z') c = 'a';
}
return 0;
}
consumer
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CHECK_MMAP_SUCCESS(_addr) \
if (_addr == MAP_FAILED) { \
perror("mmap"); \
exit(1); \
}
int main(int argc, char* argv[]) {
int fd;
char* addr;
char fileName[255] = "map.dat";
if (argc > 1) strcpy(fileName, argv[1]);
if ((fd = open(fileName, O_RDWR)) == -1) { perror("open"); exit(1); }
addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0);
CHECK_MMAP_SUCCESS(addr);
close(fd);
while (1) {
sleep(2);
printf("%c\n", addr[0]);
}
return 0;
}