HOWTO · C
mmap 함수를 사용하여 C에서 메모리에 쓰기
이 기사는 mmap() 함수에 대해 교육하고 이 함수를 사용하여 C에서 메모리에 쓰는 방법을 보여줍니다.
이 페이지의 내용
프로세스에서 mmap()는 메모리 매핑을 만듭니다. 쓰기 가능한 익명 매핑을 C 데이터용 메모리로 사용하거나, 공유 익명 매핑을 사용해 관련 프로세스 간에 변경 사항을 전달할 수 있습니다. 이 가이드에서는 여섯 인수와 매핑 실패 확인 방법을 설명하고, munmap()로 매핑을 해제하는 방법을 보여줍니다.
mmap()의 기능과 반환값
매핑은 프로세스의 가상 주소 공간 중 일정 범위를 파일, 장치 또는 익명 메모리에 연결합니다. 익명 메모리는 파일을 기반으로 하지 않으므로 변경 사항이 파일에 저장되지 않습니다. mmap()와 관련 상수를 사용하려면 <sys/mman.h>를 포함하세요.
이 함수는 여섯 인수를 받으며, 성공하면 새 매핑의 주소를 반환합니다. 실패하면 MAP_FAILED를 반환하므로 NULL이나 0이 아닌 이 실패 표시값과 비교해야 합니다. 호출이 실패하면 errno도 설정될 수 있으며, 이를 통해 실패 원인을 파악할 수 있습니다.
mmap()의 인수와 플래그 이해하기
인수는 순서대로 address, length, prot, flags, fd, offset입니다.
address는 시작 주소의 제안값입니다.NULL을 전달하면 커널이 적절한 주소를 선택합니다. 일반적으로 애플리케이션은 이렇게 사용합니다.length는 매핑할 바이트 수입니다. 커널이 매핑 범위의 페이지 정렬을 처리하지만, 매핑을 해제할 때는 요청한 길이를 일관되게 사용하세요.prot는 접근 권한을 설정합니다. 읽고 수정할 데이터에는PROT_READ와PROT_WRITE를 함께 지정하세요. 실행 가능한 메모리가 필요한 경우에만PROT_EXEC를 추가합니다.flags는 공유 방식과 메모리의 기반을 선택합니다.MAP_PRIVATE를 사용하면 변경 사항이 해당 프로세스에만 적용되고,MAP_SHARED를 사용하면 공유 매핑을 통해 변경 사항을 볼 수 있습니다.MAP_ANONYMOUS는 파일을 기반으로 하지 않는 메모리를 만듭니다. 이 플래그는 보통 두 공유 방식 플래그 중 하나와 함께 사용합니다.fd는 파일 기반 매핑에서 파일을 지정합니다. 일반적인 Unix 계열 시스템에서는MAP_ANONYMOUS를 사용할 때 파일 디스크립터가 무시되므로 예제에서 흔히-1을 전달합니다.offset은 파일에서 매핑을 시작할 바이트 위치를 지정합니다. 파일 기반 매핑에서는 시스템 페이지 크기의 배수여야 합니다. 익명 매핑에는 0을 사용하세요.
특정 주소 공간을 세심하게 제어해야 하는 경우가 아니라면 MAP_FIXED 사용을 피하세요. 지정된 범위의 기존 매핑을 대체할 수 있습니다. 이러한 플래그와 fork()는 여러 Unix 계열 시스템에서 제공되지만 모든 C 환경에서 사용할 수 있는 것은 아닙니다.
mmap()로 익명 메모리에 쓰기
이 완전한 예제는 정수 다섯 개를 저장할 공간을 매핑하고, 값을 쓰고 읽은 뒤 요청한 범위와 정확히 같은 크기로 매핑을 해제합니다. MAP_ANONYMOUS를 제공하는 Unix 계열 시스템을 전제로 합니다. 성공적으로 실행되면 아래에 표시된 출력을 출력합니다.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
#include <stdio.h>
#include <sys/mman.h>
int main(void) {
const size_t count = 5;
const size_t length = count * sizeof(int);
int *values = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (values == MAP_FAILED) {
perror("mmap");
return 1;
}
for (size_t i = 0; i < count; ++i) {
values[i] = (int)(i * 10);
}
for (size_t i = 0; i < count; ++i) {
printf("[%d] ", values[i]);
}
putchar('\n');
if (munmap(values, length) == -1) {
perror("munmap");
return 1;
}
return 0;
}
[0] [10] [20] [30] [40]
매핑에 PROT_READ와 PROT_WRITE를 함께 지정했으므로 읽고 쓸 수 있습니다. 다른 프로세스가 쓰기 내용을 볼 필요가 없으므로 MAP_PRIVATE가 적절합니다. munmap()에는 매핑 주소와 처음 요청한 길이를 전달해야 합니다. 요소 개수를 전달하는 함수가 아닙니다.
프로세스 간에 익명 매핑 공유하기
MAP_SHARED로 만든 매핑을 사용하면 fork() 이후에도 변경 사항을 공유할 수 있습니다. 자식 프로세스와 부모 프로세스는 같은 매핑 영역에 접근합니다. 출력을 리디렉션했을 때 초기 줄이 중복되지 않도록 예제는 fork() 전에 출력을 비웁니다. 부모는 자식이 변경한 값을 읽기 전에 자식이 끝날 때까지 기다립니다. 앞선 예제와 마찬가지로 이 예제도 MAP_ANONYMOUS와 fork()를 지원하는 Unix 계열 환경을 전제로 합니다. 제공된 실행에서 아래에 표시된 출력이 나왔습니다.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
const size_t count = 5;
const size_t length = count * sizeof(int);
int *values = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (values == MAP_FAILED) {
perror("mmap");
return 1;
}
for (size_t i = 0; i < count; ++i) {
values[i] = (int)(i + 7);
}
printf("Initial values:");
for (size_t i = 0; i < count; ++i) {
printf(" %d", values[i]);
}
putchar('\n');
fflush(stdout);
pid_t child_pid = fork();
if (child_pid == -1) {
perror("fork");
munmap(values, length);
return 1;
}
if (child_pid == 0) {
for (size_t i = 0; i < count; ++i) {
values[i] *= 5;
}
if (munmap(values, length) == -1) {
_exit(1);
}
_exit(0);
}
int status;
if (waitpid(child_pid, &status, 0) == -1) {
perror("waitpid");
munmap(values, length);
return 1;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
fputs("child process failed\n", stderr);
munmap(values, length);
return 1;
}
printf("Updated values:");
for (size_t i = 0; i < count; ++i) {
printf(" %d", values[i]);
}
putchar('\n');
if (munmap(values, length) == -1) {
perror("munmap");
return 1;
}
return 0;
}
Initial values: 7 8 9 10 11
Updated values: 35 40 45 50 55
자식 프로세스는 공유 매핑에 값을 쓰고 종료합니다. waitpid()가 완료된 뒤 부모는 같은 변경 사항을 읽습니다. MAP_PRIVATE 매핑으로는 이러한 공유 동작을 구현할 수 없습니다. 두 프로세스는 종료하기 전에 각자 매핑을 해제합니다. 파일 기반 매핑에서 MAP_SHARED를 사용하면 매핑을 통해 변경 사항을 확인할 수 있지만, 파일에 변경 사항을 영구 저장하려면 추가 동기화와 파일 시스템 관련 고려 사항이 필요합니다.