0. 시작하기전에
- 이전 포스팅에서 mmap을 어떻게 작동하는지 알아보았다.
- 이번 포스팅에서는 mmap을 직접 사용 및 활용해보자.
1. mmap()의 반환값
- 만약 mmap()을 실행하여 성공적으로 파일을 메모리에 매핑한다면, 해당 메모리의 주소값이 반환된다.
- mmap()이 실패하는 경우에는 에러가 발생한다.
- 이를 통해, mmap()의 실행결과를 판단할 수 있다.
2. msync()
- 파일내용이 수정되면 파일이 종료될 때, 파일에 접근하여 변경된 내용을 파일에 덮어씌우는 방식으로 내용이 수정된다.
- 이를 수행하는 함수가 msync()다.
- 다음 msync()의 원문을 살펴보자.
int msync(void *start, size_t length, int flags);
▶ *start
- mmap() 실행으로부터 반환받은 메모리의 시작주소
▶ length
- 동기화 할 길이
- 전체 파일을 덮어씌울 수도 있지만, 변경된 부분만 수정할 수 도 있다.
- 이 경우, 시작주소부터 동기화할 부분까지의 길이만을 입력한다.
- 그러나 일반적으로 mmap() 실행시 사용한 길이를 입력한다.
▶ flags
- 동기화 방식을 설정한다.
- MS_ASYNC(비동기 방식으로 동기화 / 동기화 명령을 하달하고 결과에 상관없이 다음 작업을 수행한다.)
- MS_SYNC(동기적 방식으로 동기화 / 동기화 명령을 처리할 때까지 대기상태가 된다.)
- MS_INVALID(현재 메모리 맵의 데이터를 무시하고, 기존의 파일 데이터로 다시 덮어씌운다.)
3. 실습예제 - 파일 읽기
- 우선 매핑될 파일을 만들어주자.
- link.txt라는 이름으로 만들어준다.
Hello, world!
- 다음 mmap() 프로그램을 작성한다.
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
int main(int argc, const char *argv[]) {
char *filepath = "link.txt"; // 매핑할 파일
struct stat fileInfo; // 파일 정보를 담을 구조체
int fd;
fd = open(filepath, O_RDONLY, (mode_t)0600); // 파일 데이터를 fd에 담음
if (fd < 0) { // 파일 읽기 실패시 에러
printf("can't open file\n");
exit(EXIT_FAILURE);
}
fstat(fd, &fileInfo); // fstat(): fd를 구조체에 넣음
printf("File size is %ji\n", (intmax_t)fileInfo.st_size); // 파일 데이터의 크기를 출력
char *map = mmap(0, fileInfo.st_size, PROT_READ, MAP_SHARED, fd, 0);
// 파일을 메모리에 매핑
// 파일 처음부터(0), 파일 사이즈만큼(st_size), 읽기만 가능(PROT_READ), 다른 프로세스 공유가능(SHARED)
// 매핑할 파일(fd), offset(0)
// 즉, 파일 전체를 매핑하라는 의미
// mmap()이 성공하면, *map에는 매핑된 메모리 주소값이 들어간다.
if(map == MAP_FAILED) { // 매핑에 실패하여 에러메세지가 담긴 경우
close(fd);
perror("Error mmaping the file");
exit(EXIT_FAILURE);
}
// no need of scheduling, just access memory
for (off_t i = 0; i < fileInfo.st_size; i++) {
printf("Found character %c at %ji\n", map[i], (intmax_t)i);
} // 매핑된 파일의 데이터(문자열 = 배열)를 하나씩 읽어온다.
if (munmap(map, fileInfo.st_size) == -1) { // 매핑 해제
close(fd);
perror("Error un-mmapping the file");
exit(EXIT_FAILURE);
}
close(fd);
return 0;
}
- 위의 사진은 실행 결과다.
- 실행결과를 보면 총 13개의 데이터가 출력된 것을 확인할 수 있다.
- 마지막 데이터는 공백을 의미한다.
- 각 데이터는 1바이트이며, 총 13바이트의 데이터 라는 것을 확인할 수 있다.
4. 실습예제 - 파일 쓰기
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
int main(int argc, const char *argv[]) {
struct stat fileInfo;
char *filepath = "link.txt"; // 매핑할 파일
char *update = "hello, mmap!!!"; // 업데이트할 내용
int fd = open(filepath, O_RDWR, (mode_t)0600); // 파일 데이터를 fd에 담음(읽기 쓰기 가능)
if (fd == -1) { // 파일 읽기 실패시 에러
printf("Error opening file for writing\n");
exit(EXIT_FAILURE);
}
fstat(fd, &fileInfo); // fstat(): fd를 구조체에 넣음
printf("%ld\n", fileInfo.st_size); // 파일 사이즈를 출력
char *map = mmap(0, fileInfo.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// 파일을 메모리에 매핑(읽기 쓰기 가능)
if(map == MAP_FAILED) { // 매핑에 실패하여 에러메세지가 담긴 경우
close(fd);
perror("Error mmaping the file");
exit(EXIT_FAILURE);
}
printf("%ld", strlen(update)); // 업데이트할 데이터의 길이를 출력
for(size_t i = 0; i < strlen(update); i++) {
printf("Writing character %c at %zu\n", update[i], i);
map[i] = update[i]; // 업데이트 할 문자열을 매핑된 데이터와 교체
}
if (msync(map, fileInfo.st_size, MS_SYNC) == -1) {
perror("Could not sync the file to disk");
}
if (munmap(map, fileInfo.st_size) == -1) { // 매핑 해제
close(fd);
perror("Error un-mmapping the file");
exit(EXIT_FAILURE);
}
close(fd);
return 0;
}
- 성공적으로 파일이 업데이트된 것을 확인할 수 있다.
a) 알아두자!
- munmap()을 사용하지 않더라도, 자동으로 sync작업이 수행된다.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
int main(int argc, const char *argv[]) {
struct stat fileInfo;
char *filepath = "link.txt"; // 매핑할 파일
char *update = "No munmap!!!"; // 업데이트할 내용
int fd = open(filepath, O_RDWR, (mode_t)0600); // 파일 데이터를 fd에 담음(읽기 쓰기 가능)
if (fd == -1) { // 파일 읽기 실패시 에러
printf("Error opening file for writing\n");
exit(EXIT_FAILURE);
}
fstat(fd, &fileInfo); // fstat(): fd를 구조체에 넣음
printf("%ld\n", fileInfo.st_size); // 파일 사이즈를 출력
char *map = mmap(0, fileInfo.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// 파일을 메모리에 매핑(읽기 쓰기 가능)
if(map == MAP_FAILED) { // 매핑에 실패하여 에러메세지가 담긴 경우
close(fd);
perror("Error mmaping the file");
exit(EXIT_FAILURE);
}
printf("%ld", strlen(update)); // 업데이트할 데이터의 길이를 출력
for(size_t i = 0; i < strlen(update); i++) {
printf("Writing character %c at %zu\n", update[i], i);
map[i] = update[i]; // 업데이트 할 문자열을 매핑된 데이터와 교체
}
close(fd);
return 0;
}
- 위의 코드는 munmap()과 msync()없이 파일에 쓰기 작업을 실행한다.
- 위의 결과처럼, munmap()과 msync()를 사용하지 않아도 작업을 완료하면 자동으로 동기화를 수행하는 것을 확인할 수 있다.
'컴퓨터공학기초 개념 > 시스템 프로그래밍' 카테고리의 다른 글
37. 시스템 프로그래밍 - 파일시스템 관련 시스템 콜 (0) | 2021.09.27 |
---|---|
35. 시스템 프로그래밍 - 메모리 & mmap (3) | 2021.09.27 |
34. 스레드 - detach와 mutex (0) | 2021.09.25 |
33. 스레드 - 이해와 기본 (0) | 2021.09.24 |
32. 쉘 스크립트 - 현업 예제(backup, log, tar, find) (0) | 2021.09.24 |
댓글