2012-08-17 2 views
1

프로세스와 공유 메모리 사용에 대한 몇 가지 질문이 있습니다. 이전 게시물을 여러 번 보았으므로 답변을 정확하게 수집하지 못했습니다. 귀하의 도움에 미리 감사드립니다.Linux/x86_64의 프로세스 간 공유 메모리

  1. 다음과 같이 shm_open + mmap을 사용하고 있습니다. 이 코드는 부모와 자식이 교대로 g_shared-> count를 번갈아 사용하는 것으로 의도 된대로 작동합니다 (동기화는 이식성이 없으며 특정 메모리 모델에서만 작동하지만 지금은 충분한 경우). 그러나 MAP_SHARED를 MAP_ANONYMOUS로 변경하면 | MAP_SHARED, 메모리가 공유되지 않고 '플래그'가 뒤집히지 않기 때문에 프로그램이 멈 춥니 다. 플래그를 제거하면 0에서 10까지 세는 각 프로세스의 상황을 확인할 수 있습니다 (각 프로세스에는 구조의 자체 복사본이 있으므로 '카운트'필드가 있음을 의미 함). 이것은 예상 된 행동입니까? 나는 메모리가 파일에 의해 뒷받침되는 것을 원하지 않는다. 프로세스 대신 스레드 인 경우 발생할 수있는 일을 에뮬레이트하고 싶습니다 (다른 이유로 프로세스가되어야합니다).

  2. 정말 shm_open이 필요합니까? 프로세스가 동일한 계층 구조에 속하기 때문에 대신 mmap을 단독으로 사용할 수 있습니까? 나는 이것이 '간부'가 없다면 이것은 매우 간단 할 것이라고 이해한다. 그러나 '포크'다음에 '간부'가있을 때 그것을 어떻게 작동시킬 수 있을까?

  3. x86_64 (Intel i7-2600)에서 커널 버전 3.2.0-23을 사용하고 있습니다. 이 구현에서 mmap는 동일한 전역 객체를 공유하는 pthread와 공유 메모리와 동일한 동작 (정확성 및 성능)을 제공합니까? 예를 들어, MMU는 세그먼트를 '캐시 가능'MTRR/TLB 속성으로 매핑합니까?

  4. cleanup_shared() 코드가 맞습니까? 기억이 새지 않니? 어떻게 확인할 수 있습니까? 예를 들어, System V의 'ipcs'와 동일한 기능이 있습니까?

덕분에, /Doobs

shmem.h :

#ifndef __SHMEM_H__ 
#define __SHMEM_H__ 

//includes 

#define LEN 1000 
#define ITERS 10 

#define SHM_FNAME "/myshm" 

typedef struct shmem_obj { 
    int count; 
    char buff[LEN]; 
    volatile int flag; 
} shmem_t; 

extern shmem_t* g_shared; 
extern char proc_name[100]; 
extern int fd; 

void cleanup_shared() { 
    munmap(g_shared, sizeof(shmem_t)); 
    close(fd); 
    shm_unlink(SHM_FNAME); 
} 

static inline 
void init_shared() { 
    int oflag; 

    if (!strcmp(proc_name, "parent")) { 
     oflag = O_CREAT | O_RDWR; 
    } else { 
     oflag = O_RDWR; 
    } 

    fd = shm_open(SHM_FNAME, oflag, (S_IREAD | S_IWRITE)); 
    if (fd == -1) { 
     perror("shm_open"); 
     exit(EXIT_FAILURE); 
    } 

    if (ftruncate(fd, sizeof(shmem_t)) == -1) { 
     perror("ftruncate"); 
     shm_unlink(SHM_FNAME); 
     exit(EXIT_FAILURE); 
    } 

    g_shared = mmap(NULL, sizeof(shmem_t), 
        (PROT_WRITE | PROT_READ), 
        MAP_SHARED, fd, 0); 
    if (g_shared == MAP_FAILED) { 
     perror("mmap"); 
     cleanup_shared(); 
     exit(EXIT_FAILURE); 
    } 
} 

static inline 
void proc_write(const char* s) { 
    fprintf(stderr, "[%s] %s\n", proc_name, s); 
} 

#endif // __SHMEM_H__ 

shmem1.c (부모 프로세스) :

#include "shmem.h" 

int fd; 
shmem_t* g_shared; 
char proc_name[100]; 

void work() { 
    int i; 
    for (i = 0; i &lt ITERS; ++i) { 
     while (g_shared->flag); 
     ++g_shared->count; 
     sprintf(g_shared->buff, "%s: %d", proc_name, g_shared->count); 
     proc_write(g_shared->buff); 
     g_shared->flag = !g_shared->flag; 
    } 
} 

int main(int argc, char* argv[], char* envp[]) { 
    int status, child; 
    strcpy(proc_name, "parent"); 
    init_shared(argv); 
    fprintf(stderr, "Map address is: %p\n", g_shared); 

    if (child = fork()) { 
     work(); 
     waitpid(child, &status, 0); 
     cleanup_shared(); 
     fprintf(stderr, "Parent finished!\n"); 
    } else { /* child executes shmem2 */ 
     execvpe("./shmem2", argv + 2, envp); 
    } 
} 

shmem2.c (자식 프로세스) :

#include "shmem.h" 

int fd; 
shmem_t* g_shared; 
char proc_name[100]; 

void work() { 
    int i; 
    for (i = 0; i &lt ITERS; ++i) { 
     while (!g_shared->flag); 
     ++g_shared->count; 
     sprintf(g_shared->buff, "%s: %d", proc_name, g_shared->count); 
     proc_write(g_shared->buff); 
     g_shared->flag = !g_shared->flag; 
    } 
} 

int main(int argc, char* argv[], char* envp[]) { 
    int status; 
    strcpy(proc_name, "child"); 
    init_shared(argv); 
    fprintf(stderr, "Map address is: %p\n", g_shared); 
    work(); 
    cleanup_shared(); 
    return 0; 
} 
+0

리눅스가 공유 메모리를 구현하기 위해 사용하는'tmpfs '는 가상 파일 시스템입니다. 거기에있는 파일의 내용은 전체적으로 메모리에 존재하며,'mmap'은'tmpfs'가 공유 메모리 "파일"의 내용을 저장하는 데 사용하는 동일한 물리적 페이지를 직접 매핑합니다. 'shm_open '은'/ dev/shm' (또는'tmpfs'가 마운트 된 곳)에 파일을 만들고/열고 파일 디스크립터를 되돌려주는 역할을합니다. 'open (..., O_CREAT)'를 사용하여 파일을 만들 수는 있지만 이식 할 수는 없습니다. 열린 파일 기술자는'execve'에서 살아남지 만 매핑은 그렇지 않습니다. –

답변

3
  1. MAP_ANONYMOUS를 전달하면 커널이 파일 설명자 인수를 무시하고 대신 사적인 매핑을 제공합니다. 그것은 당신이 원하는 것이 아닙니다.

  2. 예, 부모 프로세스 인 fork 프로세스에서 익명 공유 매핑을 만들고 자식 프로세스가 매핑을 상속하고 부모 및 다른 모든 자식과 메모리를 공유하도록 할 수 있습니다. 그 obvoiusly exec() 생존하지 않습니다.

  3. 이 질문을 이해할 수 없습니다. pthreads는 메모리를 할당하지 않습니다. 캐시 가능성은 매핑 한 파일 설명자에 따라 다릅니다. 디스크 파일 또는 익명 매핑 인 경우 캐시 가능한 메모리입니다. 비디오 프레임 버퍼 장치라면 아마 그렇지 않을 수도 있습니다.

  4. munmap()을 호출하는 올바른 방법이지만, 그 이상의 논리는 검증하지 않았습니다. 모든 프로세스의 매핑을 해제해야하며 하나만 연결 해제해야합니다./) (포크에 의해 생성 된 자식 프로세스 FD 통과 후 부모 프로세스

    int const shm_fd = shm_open(fn,...); 
    shm_unlink(fn); 
    

    하고 : 일종의 중간 지로

+0

감사합니다. Andy. 더 명확히하기 : 2. 분명히 exec()에서 살아남지는 못합니다. 이것은 제 문제입니다. 해킹 할 수 있을까요? MAP_ANONYMOUS가 매핑을 끝내고이를 명시 적으로 하위 프로세스에 전달하는 주소를 파생시킬 수 있습니까? 3.이 질문을 이해할 수 없습니다. 예제 코드에 표시된 공유 메모리를 구체적으로 묻습니다. '* g_shared'가 캐시됩니다. 반대로, pthreads 일 때, 객체는 쓰레드가 공유하는 글로벌 세그먼트에 할당되어 캐시됩니다. 나는 위와 같이 MAP_SHARED를 사용한다고 할지라도 귀하의 답변이 제안한다고 생각합니다. – user1605883

+0

@ user1605883,'mmap (NULL, 길이, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0)'는'fork'에서 생존 할 수있는 copy-on-write 공유 익명 매핑을 생성하지만'execve'에서는 생성되지 않습니다. 'execve' (모든'exec *()'라이브러리 호출을 뒷받침하는 syscall)은 새로운 페이지 테이블을 설치하므로 기존의 모든 매핑을 파괴합니다. –

0

2B)는, 그 호출 할 수있다 argp 또는 envp를 통해 execve()를 실행하십시오. 이 유형의 열린 파일 디스크립터는 fork()/execve()에서도 유지되므로 부모 프로세스와 저장된 프로세스 모두에서 fd를 mmap 할 수 있습니다. 여기에 더 완전한 코드 예제는 복사 된 단순화/코드에서 소독 내가 우분투 12.04에서 성공적으로 실행/리눅스 커널 3.13/glibc는 2.15 : 괜찮 사양 - 현명를 제거 인 경우에

int create_shm_fd(void) { 
    int oflags = O_RDWR | O_CREAT | O_TRUNC; 
    string const fn = "/some_shm_fn_maybe_with_pid"; 
    int fd; 
    neg_one_fail(fd = shm_open(fn.c_str(), oflags, S_IRUSR | S_IWUSR), "shm_open"); 
    if(fd == -1) { rt_err(strprintf("shm_open() failed with errno=%s", str(errno).c_str())); } 
    // for now, we'll just pass the open fd to our child process, so 
    // we don't need the file/name/link anymore, and by unlinking it 
    // here we can try to minimize the chance/amount of OS-level shm 
    // leakage. 
    neg_one_fail(shm_unlink(fn.c_str()), "shm_unlink"); 
    // by default, the fd returned from shm_open() has FD_CLOEXEC 
    // set. it seems okay to remove it so that it will stay open 
    // across execve. 
    int fd_flags = 0; 
    neg_one_fail(fd_flags = fcntl(fd, F_GETFD), "fcntl"); 
    fd_flags &= ~FD_CLOEXEC; 
    neg_one_fail(fcntl(fd, F_SETFD, fd_flags), "fcntl"); 
    // resize the shm segment for later mapping via mmap() 
    neg_one_fail(ftruncate(fd, 1024*1024*4), "ftruncate"); 
    return fd; 
    } 

그것은 나에게 분명 100 %는 아니다 FD_CLOEXEC 그리고/또는 그렇게하면 fd는 exec에서 생존 할 것이라고 가정합니다. exec의 맨 페이지는 불분명하다; 그것은 "POSIX 공유 메모리 영역은 매핑이 해제되었습니다"라고 말하지만 이전에는 매핑이 보존되지 않는 일반적인 주석과 중복되었습니다. shm_open() 'dd가 닫힐 것이라고 말하지 않습니다. 물론, 제가 언급했듯이, 코드는 적어도 하나의 경우에 작동하는 것처럼 보입니다.

내가이 접근법을 사용하는 이유는 공유 메모리 세그먼트/파일 이름 유출 가능성을 줄이는 것처럼 보일 수 있으며 메모리 세그먼트의 지속성이 필요 없다는 것을 분명히합니다.

관련 문제