I have a few questions on using shared memory with processes. I looked at several previous posts and couldn't glean the answers precisely enough. Thanks in advance for your help.
I'm using shm_open + mmap like below. This code works as intended with parent and child alternating to increment g_shared->count (the synchronization is not portable; it works only for certain memory models, but good enough for my case for now). However, when I change MAP_SHARED to MAP_ANONYMOUS | MAP_SHARED, the memory isn't shared and the program hangs since the 'flag' doesn't get flipped. Removing the flag confirms what's happening with each process counting from 0 to 10 (implying that each has its own copy of the structure and hence the 'count' field). Is this the expected behavior? I don't want the memory to be backed by a file; I really want to emulate what might happen if these were threads instead of processes (they need to be processes for other reasons).
Do I really need shm_open? Since the processes belong to the same hierarchy, can I just use mmap alone instead? I understand this would be fairly straightforward if there wasn't an 'exec,' but how do I get it to work when there is an 'exec' following the 'fork?'
I'm using kernel version 3.2.0-23 on x86_64 (Intel i7-2600). For this implementation, does mmap give the same behavior (correctness as well as performance) as shared memory with pthreads sharing the same global object? For example, does the MMU map the segment with 'cacheable' MTRR/TLB attributes?
Is the cleanup_shared() code correct? Is it leaking any memory? How could I check? For example, is there an equivalent of System V's 'ipcs?'
thanks, /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 (parent process):
#include "shmem.h" int fd; shmem_t* g_shared; char proc_name[100]; void work() { int i; for (i = 0; i < 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 (child process):
#include "shmem.h" int fd; shmem_t* g_shared; char proc_name[100]; void work() { int i; for (i = 0; i < 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; }
tmpfsthat Linux uses to implement shared memory is a pseudo filesystem. The content of the files there resides entirely in memory andmmap-ing them directly maps the same physical pages thattmpfsuses to store the content of the shared memory "files". Whatshm_opendoes is creating/opening a file in/dev/shm(or wherevertmpfsis mounted) and giving you back the file descriptor. You can create the file on your own withopen(..., O_CREAT)but it won't be portable. The open file descriptor will survive theexecvebut the mappings won't.