메모리 매핑이란 메모리의 특정 부분을 프로세스에 매핑하는 것을 의미한다.
이는 mmap() 함수를 통해 수행할 수 있으며, 같은 메모리 공간을 여러 프로세스가 동시에 매핑할 수 있다.
하지만 매핑한 주소의 값을 직접 변경 하는 것이 아니라, 매핑된 주소 위치의 레퍼런스를 수정하면 매핑된 주소의 값이 바뀌는 식으로 한 번 거쳐서 변경된다.
다음은 메뉴얼 페이지의 글이다.
mmap()은 호출 프로세스의 가상 주소 공간에 새 매핑을 만듭니다. 새 매핑의 시작 주소는 addr에 지정됩니다. length 인수는 매핑의 길이(0보다 커야 함)를 지정합니다.
Signal은 동기화의 수단으로는 사용가능하다. but 데이터를 주고받는 수단으로는 사용 불가능하다.
메모리 매핑을 이용해서 데이터를 건들면 파일의 내용 자체가 변경된다.
- 메모리 맵핑 : 파일을 프로세스의 메모리에 맵핑
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
fildes가 가리키는 파일에서 off로 지정한 위치부터 len만큼의 데이터를 읽어 addr이 가리키는 메모리 공간에 매핑한다.
- add : 매핑할 메모리 주소 (정확한 주소를 적을 자신이 없으므로 그냥 NULL 적자)
- len : 메모리 공간의 크기 (실제로는 10 bytes만 할당해도 10 bytes를 포함하는 페이지의 단위로 할당이 된다.)
- prot : 보호모드
- PROT_READ : 읽기 허용
- PROT_WRITE : 쓰기 허용
- flags : 매핑된 데이터의 처리 방법을 지정하는 상수
- MAP_SHARED : 변경 내용 공유
- MAP_PRIVATE : 변경 내용 공유하지 않음 → 다른 프로세스와 변경 내용을 공유하지 않음
- fildes : file descriptor (오픈하지 않은 파일을 건드릴 수는 없음)
- off : file offset (페이지 크기의 배수 = 페이지 크기(4096) * N) 만약 페이지 크기 * N이 아니라면 에러가 발생함. 주소 -1 return, 쓰려면 addr == (char *)(-1) 이렇게 비교해야함 정수거나 구조체면 다른 포인터로 써줘야함 ex (int *)(-1)
→ 페이지 단위 메모리 매핑 가능
→ 매핑된 영역을 벗어나면, SIGBUS, SIGSEGV 발생
memory mapping 예제
int main(void) {
int fd;
char *addr;
fd=open("data1", O_RDWR);
addr=mmap(NULL, 512, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
printf("%s\\n", addr);
}
메모리 매핑 해제
- 해제방법
#include <sys/mman.h>
int munmap(void *addr, size_t len);
//addr : mmap의 반환값
→ 매핑을 해제할 때는 기존에 부여했던 사이즈만 넣으면 된다.
같은 공간을 매핑한 프로세스 끼리는 같은 공간을 공유한다.
보호 모드 변경
#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
💈중요
파일 크기 변경
#include <unistd.h>
int truncate(const char *path, off_t len); // 파일 크기를 len으로 만든다.
#include <unistd.h>
int ftuncate(int fildes, off_t len);
//주의! 매핑된 공간이 늘어나고 줄어드는게 아니라 실제 파일의 크기가 변경된다는 의미이다.
//그렇기 때문에 줄일때 뒷부분은 그냥 날아간다.
만약 파일 크기가 0이면 0페이지가 매핑된다.
파일을 새로 만들어서 파일의 크기가 0이면 0페이지가 매핑돼서 아무것도 읽고 쓸 수 없다
이럴 때 파일 크기 변경으로 쓸 수 있다. 그렇지 않다면 에러뜸
→ 퀴즈 5번
int main(void){
int fd, i;
int *addr;
pid_t pid;
fd=open("data1", O_RDWR|O_CREAT, 0600);
if (fork()==0){
addr=mmap(NULL, 50, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
printf("%d\\n", *(addr+4)); // 0이 출력된다. WHY?
exit(0);
}
else{
addr=mmap(NULL, 50, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
ftruncate(fd, 5*sizeof(int));
for (i=0;i<5;i++){
*(addr+i)=i;
}
}
ftruncate(fd, 3*sizeof(int)); // 다 쓰고나서 파일 크기가 줄어든다.
wait(0);
exit(0);
}
매핑된 메모리 동기화
#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);
- MS_ASYNC : 비동기적 쓰기 작업 (OS가 시간날 때 알아서 천천히 옮겨 씀)
- MS_SYNC : 동기적 쓰기 작업
실제로 mmap함수로 PROT_WRITE만 쓰더라도 파일은 RDWD로 열어줘야한다.
→ 매모리 매핑을 하려면 READ가 무. 조. 건. 들어있어야한다. 파일에 있는 내용을 가져올 수 없기 때문!
'CS(Computer Science) > UNIX' 카테고리의 다른 글
UNIX - [PIPE - 2] (0) | 2022.12.31 |
---|---|
UNIX - [PIPE] (3) | 2022.12.30 |
UNIX - [SIGNAL] (1) | 2022.12.29 |
UNIX - [소켓 프로그래밍 예제 - 2] (1) | 2022.12.27 |
UNIX - [소켓 프로그래밍 기초 - 1] (0) | 2022.12.26 |