다른 참고 사이트 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/man/2/mmap
3 mmap란? #
mmap(2)는 메모리의 내용을 파일이나 디바이스에 대응(mapping)하기 위해서 사용하는 시스템 호출이다. 여기에서는 mmap시스템 호출의 활용방안에 대해서 알아보도록 한다.
3.1 메모리관리와 mmap #
각각의 프로세스는 프로세스마다 다른 프로세스와 중복되지 않는 주소공간을 가지게 된다. 주소 공간은 최초 논리적인 3개의 세그먼트로 분할된다. 덱스트, 데이터와 스팩이 그것이다. 텍스트 세그먼트는 읽기전용으로 프로그램의 명령을 포함하고 있다. 데이터와 스택 세크먼트는 읽기,쓰기가 모두 가능한 영역이다. 차이점이라면 데이터 세그먼트에는 초기화 된 데이터와 그렇지 않은 데이터가 함께 있는데 반해, 스택 세그먼트는 실행시간에 초기화된 값들을 보관한다는 것이다. 대부분의 시스템에서는 프로세스의 실행과 함께 커널에 의해서 스택 세그먼트가 자동으로 확장된다. 데이터 세그먼트의 경우는 malloc()계열의 시스템콜을 이용해서 확장하는 것이 가능하다. 텍스트 세그먼트의 경우 디버깅과 같은 제한된 환경에서 크기의 변경이 가능하다.
3.2 mmap의 활용용도 #
메모리의 내용을 파일에 대응시켜서 얻을 수 있는 이익을 생각해 보자. 그러면 mmap의 활용 용도를 결정할 수 있을 것이다.- 메모리의 내용을 파일에 대응시킬 수 있다면 프로세스간 데이터의 교환을 위한 용도로 사용가능 할 것이다. 프로세스간 공유하고자 하는 데이터를 파일에 대응시키고 이것을 읽고 쓰면 된다. 물론 접근제어를 해줘야 하겠지만 말이다.
- 메모리의 내용을 파일에 직접 대응시킨 다면 성능향상을 생각할 수 있을 것이다. 고전적인 방법은 파일지정자를 얻어서 직접 입출력하는 방식으로 open, read, write, lseek와 같은 함수를 이용한다. 이러한 함수의 사용은 당연하지만 상당한 비용을 지불해야 하는데, mmap를 이용하면 비용을 줄일 수 있다. 자세한 내용은 뒤에 따로 다루도록 하겠다.
3.3 mmap설명 #
mmap는 다음과 같이 선언되어 있다.#include <sys/mman.h>
#ifdef _POSIX_MAPPED_FILES
void * mmap(void *start, size_t length, int prot ,
int flags, int fd, off_t offset);
int munmap(void *start, size_t length);
#endif
mmap함수는 start부터 length까지의 메모리 영역을 열린파일 fd에 대응한다. 대응 할때 파일의 위치를 지정할 수 있는데 이것은 offset을 통해서 이루어진다.
- PROT_EXEC 페이지(page)는 실행될 수 있다.
- PROT_READ 페이지는 읽혀질 수 있다.
- PROT_WRITE 페이지는 쓸수 있다.
- PROT_NONE 페이지를 접근할 수 없다.
#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> #include <fcntl.h> int main(int argc, char **argv) { int fd; char *file = NULL; struct stat sb; char buf[80] ={0x00,}; int flag = PROT_WRITE | PROT_READ; if (argc < 2) { fprintf(stderr, "Usage: input\n"); exit(1); } if ((fd = open(argv[1], O_RDWR|O_CREAT)) < 0) { perror("File Open Error"); exit(1); } if (fstat(fd, &sb) < 0) { perror("fstat error"); exit(1); } file = (char *)malloc(40); // mmap를 이용해서 열린 파일을 메모리에 대응시킨다. // file은 대응된 주소를 가리키고, file을 이용해서 필요한 작업을 // 하면 된다. if ((file = (char *) mmap(0, 40, flag, MAP_SHARED, fd, 0)) == -1) { perror("mmap error"); exit(1); } printf("%s\n", file); memset(file, 0x00, 40); close(fd); }일단 test.nmap 이란 파일을 만들고 내용은 "hello world\n"정도로 채운다. 그 다음 위 코드를 컴파일 한후 테스트를 해보도록 하자. 파일의 내용이 메모리에 대응되는 것을 확인 할 수 있을 것이다. 코드의 마지막 부분에서 memset()을 호출했는데, 실제 파일에 대응되어서 파일 내용이 0x00으로 채워졌는지 확인해 보도록 하자.
5 mmap을 이용한 프로세스간 데이터 공유 #
mmap()은 파일 I/O에서의 우월한 성능과 메모리의 공유 가능하다는 장점을 가지므로 '파일 I/O 대체 와 IPC 용으로의 사용이 가능할 것이다. 이중 IPC용으로의 사용에 대해서 알아보도록 하겠다.
+---+---+---+---+---+ +---+ |num|값0|값1|값2|값3| |값9| +---+---+---+---+---+....+---+
5.1 예제 #
아직 완성되지 않았음#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#define VSIZE 5
#define MODE S_IRUSR|S_IWUSR
#define FLAG O_RDWR|O_CREAT|O_EXCL
typedef struct __M_que
{
int number;
int idx;
int value[VSIZE];
} M_que;
static int semid;
static struct sembuf mysem_open = {0, -1, SEM_UNDO};
static struct sembuf mysem_close = {0, 1, SEM_UNDO};
int main(int argc, char **argv)
{
int fd, i = 0;
char *file = NULL;
struct stat sb;
char buf[80] ={0x00,};
int flag = PROT_WRITE | PROT_READ;
M_que *lque;
int sem_num = 1;
if (argc < 2)
{
fprintf(stderr, "Usage: input\n");
exit(1);
}
if ((fd = open(argv[1], FLAG, MODE)) < 0)
{
perror("File Open Error");
exit(1);
}
semid = semget((key_t)234, sem_num, 0660|IPC_CREAT);
if (semid == -1)
{
perror("semget error");
exit(0);
}
lque = (void *)malloc(sizeof(M_que));
memset((void *)lque, 0x00, sizeof(M_que));
write(fd, (void *)lque, sizeof(M_que));
// mmap를 이용해서 열린 파일을 메모리에 대응시킨다.
// file은 대응된 주소를 가리키고, file을 이용해서 필요한 작업을
// 하면 된다.
if (((void *)lque =
mmap(0, sizeof(&lque), flag, MAP_SHARED, fd, 0)) == MAP_FAILED)
{
perror("mmap error");
exit(1);
}
while(1)
{
record_lock(fd);
input_value(lque, i, fd);
record_unlock(fd);
sleep(1);
i++;
}
munmap((void *)lque, sizeof(lque));
close(fd);
}
int input_value(M_que *lque, int value, int fd)
{
if (lque->number == VSIZE)
{
printf("buffer overflower\n");
record_unlock(fd);
if (semop(semid, &mysem_open, 1) == -1)
{
perror("semop error");
return -1;
}
printf("close wait\n");
semop(semid, &mysem_close, 1);
record_lock(fd);
}
printf("%d : %d\n", lque->idx, value);
lque->value[lque->idx] = value;
lque->number ++;
lque->idx = (lque->idx ++) % VSIZE;
record_unlock(fd);
return 1;
}
int record_lock(int fd)
{
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 1;
return fcntl(fd, F_SETLKW, &lock);
}
int record_unlock(int fd)
{
struct flock lock;
lock.l_type = F_UNLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 1;
return fcntl(fd, F_SETLK, &lock);
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#define VSIZE 10
#define MODE S_IRUSR
#define FLAG O_RDONLY
typedef struct __M_que
{
int number;
int idx;
int value[VSIZE];
} M_que;
static int semid;
static struct sembuf mysem_open = {0, -1, SEM_UNDO};
static struct sembuf mysem_close = {0, 1, SEM_UNDO};
int main(int argc, char ** argv)
{
int fd;
char *file = NULL;
int flag = PROT_READ;
int sem_num = 0;
M_que *lque;
int value;
if (argc < 2)
{
fprintf(stderr, "Usage: ./a2 [mmap_file]\n");
exit(1);
}
if ((fd = open(argv[1], FLAG, MODE)) < 0)
{
perror("FILE Open Error");
exit(0);
}
semid = semget((key_t)234, sem_num, 0660);
if (semid == -1)
{
perror("semget error");
exit(0);
}
lque = (void *)malloc(sizeof(M_que));
if (((void *)lque =
mmap(0, sizeof(&lque), flag, MAP_SHARED, fd, 0)) == MAP_FAILED)
{
perror("mmap error");
exit(1);
}
else
{
printf("Success\n");
}
while(1)
{
record_lock(fd);
printf("%d : %d\n", lque->number, lque->idx);
if (lque->number == 5)
{
printf("Wait\n");
if(semop(semid, &mysem_open, 1) == -1)
{
perror("semop error ");
exit(0);
}
printf("OK\n");
semop(semid, &mysem_close, 1);
}
record_unlock(fd);
getchar();
}
}
int record_lock(int fd)
{
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 1;
return fcntl(fd, F_SETLKW, &lock);
}
int record_unlock(int fd)
{
struct flock lock;
lock.l_type = F_UNLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 1;
return fcntl(fd, F_SETLK, &lock);
}