2017-01-06 5 views
2

16 바이트의 공유 파일을 생성하는 프로그램을 만들고 나서 매 초마다 각 바이트의 값을 보여주는 실험을하고 있습니다.이상한 mmap 동작

프로그램이 실행되는 동안 파일을 변경하고 프로그램이 변경 사항을 인식하도록하고 싶습니다.

프로그램은 파일에 실제로 쓰는 메모리에 쓰는 것처럼 보입니다. 하지만 런타임에 파일을 수정하면 파일이 변경 되더라도 이전 값을 계속 사용하고 파일이 업데이트되지 않습니다.

다음은 변경 사항을 인식하지 못하는 버전입니다.

#include <stdio.h> 
#include <stdint.h> 
#include <string.h> 
#include <stdlib.h> 
#include <sys/mman.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <signal.h> 

// | bag of global state. 
struct { 
    uint8_t *state; 
    char *filename; 
    int length; 
} global = { 
    (uint8_t *)0, 
    "state.bin", 
    16 
}; 

// | clean up when ctrl-c is pressed. 
static void onSIGINT(int unused) 
{ 
    puts("caught SIGINT; cleaning up."); 
    munmap(global.state, global.length); 
    unlink(global.filename); 
    exit(0); 
} 

// | display each byte of global shared memory 
static void inspect() 
{ 
    int i; 

    for (i = 0; i < global.length; ++i) { 
     printf("state[%d] = %d.\n", i, global.state[i]); 
    } 
} 

int main(int argc, char **argv) 
{ 
    /* anonymous scope: initialize shared memory */ 
    { 
     // | mmap arguments 
     void *addr = (void *)0; 
     int prot = PROT_READ | PROT_WRITE; 
     int flags = MAP_SHARED; 
     int offset = 0; 
     int fd = open(global.filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); 

     // | initialize memory to 16 zerod out bytes. 
     uint8_t dummy[16]; 
     memset(&dummy, 0, global.length); 
     write(fd, dummy, global.length); 

     global.state = (uint8_t *)mmap (
      addr, 
      global.length, 
      prot, 
      flags, 
      fd, 
      offset 
     ); 

     if (global.state == MAP_FAILED) { 
      close(fd); 
      perror("could not create map.\n"); 
      unlink(global.filename); 
     }     
    } 

    signal(SIGINT, onSIGINT); 

    /* anonymous scope: mainloop */ 
    { 
     int count = 0; 

     for (;;) { 
      system("clear"); 
      printf("refresh number: %d.\n", count); 
      ++count; 
      inspect(); 
      sleep(1); 
     } 
    } 

    return 0; 
} 

여기에는 실제로 각 공유 파일을 사용하고 있음을 보여주기 위해 각 디스플레이 반복마다 각 바이트가 증가하는 버전이 있습니다. 성공적 내가 정력에 바이트 중 하나를 변경할 순간까지 각 바이트를 증가 유지하는 것이

#include <stdio.h> 
#include <stdint.h> 
#include <string.h> 
#include <stdlib.h> 
#include <sys/mman.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <signal.h> 

// | bag of global state. 
struct { 
    uint8_t *state; 
    char *filename; 
    int length; 
} global = { 
    (uint8_t *)0, 
    "state.bin", 
    16 
}; 

// | clean up when ctrl-c is pressed. 
static void onSIGINT(int unused) 
{ 
    puts("caught SIGINT; cleaning up."); 
    munmap(global.state, global.length); 
    unlink(global.filename); 
    exit(0); 
} 

// | prints length bytes starting at address given. 
static void inspect() 
{ 
    int i; 

    for (i = 0; i < global.length; ++i) { 
     printf("state[%d] = %d.\n", i, global.state[i]); 
     ++global.state[i]; 
    } 
} 

int main(int argc, char **argv) 
{ 
    /* anonymous scope: initialize shared memory */ 
    { 
     // | mmap arguments 
     void *addr = (void *)0; 
     int prot = PROT_READ | PROT_WRITE; 
     int flags = MAP_SHARED; 
     int offset = 0; 
     int fd = open(global.filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); 

     // | initialize memory to 16 zerod out bytes. 
     uint8_t dummy[16]; 
     memset(&dummy, 0, global.length); 
     write(fd, dummy, global.length); 

     global.state = (uint8_t *)mmap (
      addr, 
      global.length, 
      prot, 
      flags, 
      fd, 
      offset 
     ); 

     if (global.state == MAP_FAILED) { 
      close(fd); 
      perror("could not create map.\n"); 
      unlink(global.filename); 
     }     
    } 

    signal(SIGINT, onSIGINT); 

    /* anonymous scope: mainloop */ 
    { 
     int count = 0; 

     for (;;) { 
      system("clear"); 
      printf("refresh number: %d.\n", count); 
      ++count; 
      inspect(); 
      sleep(1); 
     } 
    } 

    return 0; 
} 

참고. 파일을 덮어 쓰면 파일 수정이 중지되지만 관계없이 계속 계산됩니다.

왜 이런 식으로 행동합니까? 예상대로 작동하게하려면 어떻게해야합니까?

+0

이 작동 안 첫 번째 프로그램이 되었습니까? 나는 그것을 실행하고 Vim으로 파일을 수정했다. 업데이트 된 값을 보여주었습니다. 예 : * :'state [1] = 50.'을 'NUL'중 하나를 '2'로 바꿀 때. – giusti

+0

O.o 그것은 나를 위해 작동하지 않는 것 ... 나를 다시 시도하자. 나는 두 대의 컴퓨터에서 시도해 보았고 0 : – Dmitry

+0

예, 둘 다 내 컴퓨터에서 작동하지 않습니다. 데비안의 다른 우분투 중 하나를 다시 확인했습니다. – Dmitry

답변

2

이는 vim에서 백업 파일을 만드는 방법으로 인해 발생합니다. 이 내용은 backupcopy 옵션에 대해 설명되어 있으며 일반적인 기본값은 원본 파일의 이름을 바꾸고 복사본을 편집하는 것입니다. 이로 인해 메모리 맵핑 된 뷰가 편집중인 파일이 아닌 백업과 연관됩니다. 당신은 당신이 파일의 아이 노드를 선택하면 이런 일이 볼 수

:

$ echo z>a && ls -i a 
14551885 a 
$ vim a 
$ ls -i a 
14551887 a 

당신이 아이 노드를 볼 수 있듯이 지금은 다르다. 예를 들어 python을 사용하여 파일을 열고 수정 한 경우 파일을 현재 위치에서 편집 할 수 있으며 예상대로 작동합니다.

예 대신 파이썬을 사용하여 :

$ ls -i a 
14551886 a 
$ python 
Python 2.7.6 (default, Oct 26 2016, 20:30:19) 
[GCC 4.8.4] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> f = open('a', 'r+') 
>>> f.write('22') 
>>> f.close() 
>>> 

$ ls -i a 
14551886 a 
+0

Vim으로'/ tmp/state.bin' 파일을 편집했지만 파일이'~/state.bin' 일 때 왜 실패 했나요? – giusti

+2

큰 관찰; 파일에 쓰기 전에': set backupcopy = yes'라고 입력하면 예상대로 작동합니다. (http://unix.stackexchange.com/questions/36467/why-inode-value-changes-when-we-edit-in-vi-editor). – Dmitry

+0

새로운 inode를 생성하지 않고 파일을 편집 할 수 있습니다. 한 가지 방법은'vim -b'를 사용하여 바이너리 모드로 여는 것입니다. 백업 복사본을 만들지 여부에 따라 다릅니다. – patthoyts