2012-08-17 80 views
1

我在使用進程共享內存方面有幾個問題。我看了幾個以前的帖子,無法精確地收集答案。在此先感謝您的幫助。Linux/x86_64上的進程之間的共享內存

  1. 我正在使用像下面這樣的shm_open + mmap。這段代碼按照父母和孩子交替增加g_shared-> count的方式工作(同步不是可移植的;它只適用於某些內存模型,但對於我現在的情況來說足夠好)。但是,當我將MAP_SHARED更改爲MAP_ANONYMOUS |時MAP_SHARED,內存不共享和程序掛起,因爲'標誌'不翻轉。刪除標誌可以確認每個從0到10的過程發生了什麼(意味着每個過程都有自己的結構副本,因此也包含'count'字段)。這是預期的行爲?我不希望內存被文件支持;我真的很想模仿可能發生的情況,如果它們是線程而不是流程(它們需要是其他原因的流程)。

  2. 我真的需要shm_open嗎?由於進程屬於同一個層次結構,我可以單獨使用mmap嗎?我知道如果沒有'exec',這將是相當直接的,但是如果在'fork'之後有'exec',我怎麼才能使它工作?

  3. 我使用的是x86_64的內核版本3.2.0-23(英特爾i7-2600)。對於這種實現,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

Linux用來實現共享內存的'tmpfs'是一個僞文件系統。這些文件的內容完全存在於內存中,並且它們直接映射「tmpfs」用於存儲共享內存「文件」內容的相同物理頁面。 'shm_open'的功能是在'/ dev/shm'(或者'tmpfs'掛載的任何地方)創建/打開一個文件,然後給你返回文件描述符。您可以使用'open(...,O_CREAT)'自己創建文件,但它不可移植。打開的文件描述符將在'execve'中存活,但映射不會。 – 2012-08-17 17:02:12

回答

3
  1. 傳遞MAP_ANONYMOUS將導致內核忽略您的文件描述符參數,併爲您提供私有映射。這不是你想要的。

  2. 是的,您可以在父進程fork中創建匿名共享映射,並讓子進程繼承映射,與父級和其他子進程共享內存。這obviusius沒有生存執行()雖然。

  3. 我不明白這個問題; pthreads不分配內存。緩存能力取決於您映射的文件描述符。如果它是磁盤文件或匿名映射,那麼它是可緩存的內存。如果它是一個視頻幀緩衝設備,它可能不是。

  4. 這是調用munmap()以正確的方式,但我沒有驗證超出邏輯。所有進程都需要取消映射,只有一個應該取消鏈接。

+0

謝謝安迪。進一步澄清: 2. 這顯然不能執行exec()雖然 這是我的問題。我可以繞過嗎?我可以導出MAP_ANONYMOUS最終映射到的地址並將其明確傳遞給子進程嗎? 3.我不明白這個問題 我特別要求示例代碼中顯示的共享內存。將'* g_shared'緩存。相反,當pthreads時,對象將被分配在由線程共享的全局段中,並因此被緩存。我認爲你的答案表明即使我使用如上所述的MAP_SHARED,它也會如此? – user1605883 2012-08-17 06:09:34

+0

@ user1605883,'mmap(NULL,length,PROT_READ | PROT_WRITE,MAP_ANONYMOUS | MAP_SHARED,-1,0)'創建一個非拷貝寫共享匿名映射,它將在fork'而不是'execve'中存活。 'execve'(支持所有'exec *()'庫調用的系統調用)安裝一個新的頁表,從而銷燬所有現有的映射。 – 2012-08-17 17:12:42

0

2B)作爲一種的中間地,有可能調用:

int const shm_fd = shm_open(fn,...); 
shm_unlink(fn); 
在父進程

,然後通過FD由叉創建子進程()/ execve()通過argp或envp。因爲此類型的打開文件描述符將在fork()/ execve()中生存,所以您可以在父進程和任何受騙進程中對fd進行mmap映射。這裏有一個更完整的代碼示例複製並簡化/從碼消毒我的Ubuntu 12.04下成功地跑/ Linux內核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真的會在執行期間生存下來。執行官的手冊頁不清楚;它說:「POSIX共享內存區域未映射」,但對我來說,這與先前的一般註釋是多餘的,映射不會保留,並且不會說shm_open()'d fd將被關閉。任何當然有事實,正如我所提到的,代碼似乎在至少一個案例中起作用。

我可能會使用這種方法的原因是它似乎減少了泄漏共享內存段/文件名的機會,並且它清楚地表明我不需要內存段的持久性。

相關問題