mmap 관련 질문입니다

ixevexi의 이미지

#include<unistd.h>
#include<stdio.h>
#include<sys/mman.h>
#include<fcntl.h>
#include<sys/stat.h>

int main(int argc, char **argv)
{
    int * a;
    int rc = open ( "mmap.obj", O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);

    char * maped = mmap(0,sizeof(int)*100,PROT_READ|PROT_WRITE,MAP_PRIVATE,rc,0);
    printf("address:%d\n",maped);
    a = (int*)maped);
    printf("%c\n",(*a)++);
    printf("%c\n",(*a)++);

    rc = msync(maped,sizeof(int)*100,MS_SYNC);
    printf("%d\n",rc);
    return 0;
}

단순히 파일을 읽어들여 메모리로 맵핑시키고
그것을 파일과 동기화 시키는 용도로 mmap을 쓰려고 합니다
그런데 man에도 안나와있고 제가 원하는 동작이랑 달라서
한번 여쭤 봅니다.

1. 저위에 mmap.obj란 파일을 생성시키는데요
이 파일의 크기가 mmap에서의 size보다 작으면,
버스 오류가 생기더라구요
정확히 이야기하면 파일 생성직후 0바이트일때 읽으려 들면
버스오류가 생깁니다. 그래서 mmap.obj를 손으로 열고
데이타를 써 넣어주니 정상 작동을 합니다.
항상 mmap에서 할당하는 크기보다 파일의 크기가 커야합니까?

2. 두번째로 저 것을 컴파일 후 실행시켰는데요
다음과 같은 결과가 나옵니다.

ixevexi@kali8:~/test$ ./a.out
address:1073840128
a
b
0
ixevexi@kali8:~/test$ ./a.out
address:1073840128
a
b
0
ixevexi@kali8:~/test$

제가 생각할때는 a->b로 바뀌고 msync로 인해 파일에
바뀐 내용이 수록되어 두번째 실행시에는 b,c,0가 나와야할 것같은데요
msync가 0을 반환하는 걸로 보아 확실히 성공은 했는데..
무엇이 문제일까요?

mmap 처음 써보는 초보가 질문 올립니다.

* 소스 수정했습니다 -_-;; 부끄러워라

hb_kim의 이미지

뭔가 메모리의 내용을 바꾼다음에 싱크를 하셔야 그게 파일에 저장될것 같군

뭔가 메모리의 내용을 바꾼다음에 싱크를 하셔야 그게 파일에 저장될것 같군요.

혹 main() 안에 있는 로컬 포인터가 프로그램 실행간에 값을 유지하기를 기대하신것은 아니겠죠?

cinsk의 이미지

mmap은 파일의 내용을 memory로 mapping시켜주는 함수입니다.

mmap은 파일의 내용을 memory로 mapping시켜주는 함수입니다. mmap()으로 파일에 내용을 덧붙이는 작업은 할 수 없습니다. 즉, mmap()을 써서 파일 크기가 0인 파일에 어떤 내용을 써 넣는 것은 불가능합니다.

실제 써 넣는 내용과 파일을 항상 동기화하는 것이 목적이라면 꼭 mmap()을 쓸 필요가 없습니다. 원하는 파일에 fsync()를 불러주면 됩니다.

mmap()을 잘못썼을 때 흔히 SIGSEGV와 SIGBUS가 발생합니다. 가장 흔한 예는 read only로 mapping한 memory등, 접근이 불가능한 곳에 access했을 경우 SIGSEGV, 현재 파일의 내용과 범위가 맞지 않는 곳을 접근하려 했을 때 SIGBUS가 발생합니다. 즉, 파일 크기가 0인데, 쓰려한다면 대개 SIGBUS가 발생합니다.

배열의 범위가 벗어나는 것을 잡아주는 debugging library인 Electric Fence(efence)는 바로 read-only로 mmap()했을 때 SIGSEGV가 발생하는 것에 힌트를 얻었습니다.

또한 실제 파일 크기보다 큰 공간을 mmap()으로 할당하면 (예: 파일 크기는 100byte이고, mmap에서 1024 byte를 할당한 경우), 나머지 1024 - 100 byte의 위치에는 0으로 채워지고, 이 곳에 쓰는 데이터는 나중에 무시됩니다. 즉 파일의 내용에 반영되지 않습니다. 앞에서 mmap()으로 파일의 크기를 변경하는 연산을 수행할 수 없다고 말한 것을 기억하기 바랍니다.

cinsk의 이미지

한가지 더, mmap()의 두번째 인자에 지정하는 길이는 보통 syste

한가지 더, mmap()의 두번째 인자에 지정하는 길이는 보통 system page size의 배수가 되어야 합니다. 주어진 예제에서 "sizeof(int)*100"의 크기로 했는데, 바람직하지 않습니다. mmap()이 에러를 내고 errno를 EINVAL로 setting할 확률이 높습니다. 따라서 이 크기는 항상 page size의 배수가 되도록 하기 바랍니다. 앞에서도 말했듯이, file size보다 같거나 크며 page size의 배수가 되도록 하고, 이 때 남은 공간에는 0이 채워지고, 여기에 쓴 내용은 무시됩니다.

System의 page size는 sysconf(3)을 써서 얻을 수 있습니다.

ixevexi의 이미지

[b]cinsk[/b]님 답변 고맙습니다 ^^mmap에서 쓸 파일은

cinsk님 답변 고맙습니다 ^^
mmap에서 쓸 파일은 항상 유효?해야하고
page size의 크기로 인자를 주어야 한다는 것 앞으로 잘 기억하겠습니다.

hb_kim 씀:
뭔가 메모리의 내용을 바꾼다음에 싱크를 하셔야 그게 파일에 저장될것 같군요.

혹 main() 안에 있는 로컬 포인터가 프로그램 실행간에 값을 유지하기를 기대하신것은 아니겠죠?

음 전 먼가 바꾼줄 알았는데요 ^^

소스가 어이 없게 이상해 져버렸지만
분명히 mmap으로 할당한 공간을
int * a; 포인터로 포인팅 하고
(*a)를 변경하면 그 mmap으로 할당된 부분이 변경되는 것 아닌가요?

제 생각에는 코드는 틀린 것이 없어보이는데요??

#include<unistd.h>
#include<stdio.h>
#include<sys/mman.h>
#include<fcntl.h>
#include<sys/stat.h>

typedef struct char_100
{
    char character[100];
}RECORD;

int main(int argc, char **argv)
{
    int rt;
    RECORD rc, *prc;


    int fd = open ( "mmap.obj", O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);

    printf("page size:%ld\n", sysconf(_SC_PAGESIZE));
    prc=(RECORD *) mmap(0,4096,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
    printf("address:%d\n",prc);

    printf("%c\n",prc->character[0]);
    prc->character[0]='b';
    printf("%c\n",prc->character[0]);

    rt = msync(prc,sizeof(struct char_100),MS_SYNC);
    munmap(prc,sizeof(struct char_100));
    printf("%d\n",rt);
    close(fd);
    return 0;
}

저 코드가 맘에 안들어서 다른 방식으로 한번 해보았습니다.
하지만 여전합니다 -_-;; 저위 4096은 제 시스템에서 page size입니다.

버그소년의 이미지

flags 값을 MAP_SHARED로 해야합니다.MAP_PRIVA

flags 값을 MAP_SHARED로 해야합니다.

MAP_PRIVATE는 원본 파일이 변경되지 않습니다.

man페이지 활용... ^^;

ixevexi의 이미지

감사드립니다 ^^ 되네요 ^^

감사드립니다 ^^
되네요 ^^

댓글 보기 옵션

원하시는 댓글 전시 방법을 선택한 다음 "설정 저장"을 누르셔서 적용하십시오.