【IPC】Posix共享内存区与mmap内存映射

来源:互联网 发布:网络强国与青年运动 编辑:程序博客网 时间:2024/05/17 09:45

共享内存是一种IPC形式,与其它IPC机制如管道、消息队列等相比,数据不必在进程与内核间多次交换,进程间通信的速度更快。当共享内存区映射到共享它的进程的地址空间时,再加以一些同步控制,这些进程就可以进行数据传送了。mmap函数提供了内存映射功能,可以把一个文件或一个Posix共享内存区对象映射到调用进程的地址空间,下面首先介绍mmap的用法。

1、mmap

#include <sys/mman.h>void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);int munmap(void *addr, size_t length);int msync(void *addr, size_t length, int flags);

mmap可使用普通文件以提供内存映射IO,或者是特殊文件以提供匿名内存映射,还可以使用shm_open以提供无亲缘关系进程间的Posix共享内存区,成功时返回指向映射区域的指针,失败时返回MAP_FAILED,即((void*)-1),并设置相应的errno。成功返回后,文件描述字fd可关闭,不影响后续共享内存的使用。

参数addr指定了文件描述字fd映射到进程地址空间的起始地址,不过这个参数一般为NULL,为空时地址由内核来选择。
参数length指定了fd映射到进程地址空间的字节数,字节计数从fd的offset处开始算起,offset为从fd文件开头的字节偏移量。
参数prot指定了内存映射区的读写权限,有几个常值:PROT_EXEC、PROT_READ、PROT_WRITE、PROT_NONE,分别表示可执行、可读、可写、不可访问,前三个可使用按位或符号拼起来使用。
参数flags指定了内存映射区的共享模式,必须从MAP_SHARED与MAP_PRIVATE两者之间选择一个,前者表示当前进程修改的内存映射区对其它进程是可见的,也确实修改了文件fd并影响到其它进程,后者则恰好相反,表示当前进程修改的内存映射区对其它进程是不可见的,并没有修改文件fd也不会影响其它进程。flags除了这两个值之外,还有一些其它的可选值,用按位或符号连起来即可,不过考虑到移植安全,并不使用MAP_FIXED,参数addr也指定为NULL。

mmap用以内存映射,解除一个进程的的内存映射关系使用munmap。参数addr为mmap返回的地址,length为映射区的大小。成功时返回0,失败时返回-1,并设置相应的errno。

对于mmap函数的flags参数,为MAP_PRIVATE时,调用进程对共享内存作的修改都会被丢弃掉;为MAP_SHARED时,内核维护共享内存与文件fd的内容一致,但有时候我们要手动同步共享内存与文件fd的内容,这个工作由msync函数来完成。msync成功时返回0,失败时返回-1,并设置相应的errno。参数addr为mmap返回的地址,length即映射区的大小,flags是MS_ASYNC、MS_SYNC、MS_INVALIDATE中的集合,但前两个不可同时指定。MS_ASYNC表示异步写,函数立即返回;MS_SYNC表示同步写,同步完成后函数才返回;MS_INVALIDATE表示与同一个文件fd映射的其它内存区域的原有数据将无效,以使它们立即更新。

下面以例子说明mmap的用法。

例一:fork

// increment.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>int g_count = 0;int main(int argc, char **argv){    int i, nloop;    if (2 != argc) {        printf("usage: %s <#loops>\n", argv[0]);        exit(EXIT_FAILURE);    }    nloop = atoi(argv[1]);    setbuf(stdout, NULL);    if (fork() == 0) { // child        for (i = 0; i < nloop; ++i) {            printf("child: %d\n", g_count++);        }        exit(EXIT_SUCCESS);    }    for (i = 0; i < nloop; ++i) { // parent        printf("parent: %d\n", g_count++);    }    exit(EXIT_SUCCESS);}

例一是一个计数程序,全局计数变量g_count初始值为0,计数次数nloop从命令行指定,我们都知道fork的子进程不会继承父进程的地址空间,如这里的g_count,而是一份独立的拷贝,父、子进程分别对g_count计数nloop次,并输出计数结果,程序运行结果如下,可以看出父、子进程对g_count的操作并没有互相影响。

$ gcc -o test increment.c$ ./test 10parent: 0parent: 1parent: 2parent: 3parent: 4parent: 5parent: 6parent: 7parent: 8parent: 9child: 0child: 1child: 2child: 3child: 4child: 5child: 6child: 7child: 8child: 9

例二:mmap sem

// increment2.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <semaphore.h>#include <sys/mman.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)#define SEM_NAME "/semtest"int main(int argc, char **argv){    int fd, i, nloop, zero = 0;    int *ptr;    sem_t *sem;    if (3 != argc) {        printf("usage: %s <pathname> <#loops>\n", argv[0]);        exit(EXIT_FAILURE);    }    nloop = atoi(argv[2]);    // open file    if ((fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE)) == -1) {        printf("open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // initialize fd to 0    if (write(fd, &zero, sizeof(int)) == -1) {        printf("write error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // map into memory    if ((ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // close file    if (close(fd) == -1) {        printf("close error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // create semaphore and initialize to 1    if ((sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1)) == SEM_FAILED) {        printf("sem_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // unlink semaphore    if (sem_unlink(SEM_NAME) == -1) {        printf("sem_unlink error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // stdout is unbuffered    setbuf(stdout, NULL);    if (fork() == 0) { // child        for (i = 0; i < nloop; ++i) {            if (sem_wait(sem) == -1) {                printf("sem_wait child: error: %s\n", strerror(errno));                exit(EXIT_FAILURE);            }            printf("child: %d\n", (*ptr)++);            if (sem_post(sem) == -1) {                printf("sem_post child error: %s\n", strerror(errno));                exit(EXIT_FAILURE);            }        }        exit(EXIT_SUCCESS);    }    for (i = 0; i < nloop; ++i) { // parent        if (sem_wait(sem) == -1) {            printf("sem_wait parent error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }        printf("parent: %d\n", (*ptr)++);        if (sem_post(sem) == -1) {            printf("sem_post parent error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }    }    exit(EXIT_SUCCESS);}

例二在例一的基础上增加了mmap内存映射以在进程间共享数据,因为涉及进程间的数据同步,所以还增加了Posix信号灯sem。程序中,信号灯是个有名信号灯,名字为“/semtest”,mmap所需的共享文件fd从命令行指定,打开这个文件后,初始化为0以提供计数功能,而不是例一中的一个全局变量用来计数,接着使用mmap完成内存映射,mmap的参数指定了文件读写权限和进程间共享模式,随后关闭文件,内存映射完成后关闭文件是没有影响的。关于sem信号灯,打开时初始化为1,接着调用sem_unlink,这个对后续信号灯的使用并没有影响,因为内核维护着信号灯的引用计数,只有当最后一个sem_close调用或者程序结束后信号灯才会从系统中移除。最后同样是父、子进程对计数值的操作,操作对象是mmap返回的地址,因为mmap指定了MAP_SHARED,所以进程间互相影响也确实修改了共享文件中的内容,所以计数期间加了信号灯sem_wait/sem_post以完成同步,避免冲突,否则计数结果就会出错。程序运行结果如下,可以看出父、子进程实现了计数共享。

$ gcc -o test -pthread increment2.c$ ./test count 10parent: 0parent: 1child: 2child: 3child: 4child: 5child: 6child: 7child: 8child: 9child: 10child: 11parent: 12parent: 13parent: 14parent: 15parent: 16parent: 17parent: 18parent: 19

使用file命令查看共享文件count的类型为data数据文件。

$ file count$ count: data

例三:sem_init

// increment3.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <semaphore.h>#include <sys/mman.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)struct shared{    sem_t sem;    int count;} shared;int main(int argc, char **argv){    int fd, i, nloop;    struct shared *ptr;    if (3 != argc) {        printf("usage: %s <pathname> <#loops>\n", argv[0]);        exit(EXIT_FAILURE);    }    nloop = atoi(argv[2]);    // open file    if ((fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE)) == -1) {        printf("open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // initialize fd to 0    if (write(fd, &shared, sizeof(struct shared)) == -1) {        printf("write error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // map into memory    if ((ptr = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // close file    if (close(fd) == -1) {        printf("close error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // initialize semaphore that is shared between processes    if (sem_init(&ptr->sem, 1, 1) == -1) {        printf("sem_init error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // stdout is unbuffered    setbuf(stdout, NULL);    if (fork() == 0) { // child        for (i = 0; i < nloop; ++i) {            if (sem_wait(&ptr->sem) == -1) {                printf("sem_wait child: error: %s\n", strerror(errno));                exit(EXIT_FAILURE);            }            printf("child: %d\n", ptr->count++);            if (sem_post(&ptr->sem) == -1) {                printf("sem_post child error: %s\n", strerror(errno));                exit(EXIT_FAILURE);            }        }        exit(EXIT_SUCCESS);    }    for (i = 0; i < nloop; ++i) { // parent        if (sem_wait(&ptr->sem) == -1) {            printf("sem_wait parent error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }        printf("parent: %d\n", ptr->count++);        if (sem_post(&ptr->sem) == -1) {            printf("sem_post parent error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }    }    exit(EXIT_SUCCESS);}

例二中的信号灯是个有名信号灯,信号灯还可以是基于内存映射的匿名信号灯,为此例三添加了shard结构体,成员变量为一个信号灯和一个计数器,mmap映射长度为这个结构体的长度,调用sem_init初始化信号灯的值为1,其第二个参数为1,目的是保证信号灯同步在进程间使用而非同一进程的不同线程间,其它用法不变,程序运行结果一样。

例四:MAP_ANONYMOUS

// increment_map_anon.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <semaphore.h>#include <sys/mman.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)#define SEM_NAME "/semtest"int main(int argc, char **argv){    int i, nloop;    int *ptr;    sem_t *sem;    if (2 != argc) {        printf("usage: %s  <#loops>\n", argv[0]);        exit(EXIT_FAILURE);    }    nloop = atoi(argv[1]);    // anonymous mmap    if ((ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // create, initialize, and unlink semaphore    if ((sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1)) == SEM_FAILED) {        printf("sem_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    if (sem_unlink(SEM_NAME) == -1) {        printf("sem_unlink error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // stdout is unbuffered    setbuf(stdout, NULL);    if (fork() == 0) { // child        for (i = 0; i < nloop; ++i) {            if (sem_wait(sem) == -1) {                printf("sem_wait child: error: %s\n", strerror(errno));                exit(EXIT_FAILURE);            }            printf("child: %d\n", (*ptr)++);            if (sem_post(sem) == -1) {                printf("sem_post child error: %s\n", strerror(errno));                exit(EXIT_FAILURE);            }        }        exit(EXIT_SUCCESS);    }    for (i = 0; i < nloop; ++i) { //parent        if (sem_wait(sem) == -1) {            printf("sem_wait parent error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }        printf("parent: %d\n", (*ptr)++);        if (sem_post(sem) == -1) {            printf("sem_post parent error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }    }    exit(EXIT_SUCCESS);}

例二、例三中的mmap是对一个共享文件的内存映射,为此首先要打开一个文件,不过这并不是必需的,mmap同样也提供了匿名内存映射,不用基于一个确实存在的文件来实现映射,这时mmap的flags为MAP_SHARED | MAP_ANONYMOUS,fd和offset被忽略,不过fd一般为-1,offset为0。例四在例二的基础上作了修改,通过mmap的参数变更,使用匿名内存映射,程序运行结果相同。

mmap映射长度——
使用mmap映射一个普通文件时,还有一点值得注意,那就是mmap的第二个参数,即mmap的映射长度,上面的例子中mmap的映射长度等于共享文件的大小,然而它们可以不相等。

除了匿名内存映射之外,有时候我们还可以打开一个特殊的文件“/dev/zero”,将这个文件描述字fd作为mmap的参数也是可以的,任何从这个fd读取的数据都为0,写往这个fd的数据也都被丢弃,也可以完成进程间的数据共享,例子如下。

// increment_dev_zero.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <semaphore.h>#include <sys/mman.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)#define SEM_NAME "/semtest"int main(int argc, char **argv){    int fd, i, nloop;    int *ptr;    sem_t *sem;    if (2 != argc) {        printf("usage: %s  <#loops>\n", argv[0]);        exit(EXIT_FAILURE);    }    nloop = atoi(argv[1]);    // open /dev/zero    if ((fd = open("/dev/zero", O_RDWR)) == -1) {        printf("open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // mmap    if ((ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // close fd    if (close(fd) == -1) {        printf("close error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // create, initialize, and unlink semaphore    if ((sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1)) == SEM_FAILED) {        printf("sem_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    if (sem_unlink(SEM_NAME) == -1) {        printf("sem_unlink error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // stdout is unbuffered    setbuf(stdout, NULL);    if (fork() == 0) { // child        for (i = 0; i < nloop; ++i) {            if (sem_wait(sem) == -1) {                printf("sem_wait child: error: %s\n", strerror(errno));                exit(EXIT_FAILURE);            }            printf("child: %d\n", (*ptr)++);            if (sem_post(sem) == -1) {                printf("sem_post child error: %s\n", strerror(errno));                exit(EXIT_FAILURE);            }        }        exit(EXIT_SUCCESS);    }    for (i = 0; i < nloop; ++i) { // parent        if (sem_wait(sem) == -1) {            printf("sem_wait parent error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }        printf("parent: %d\n", (*ptr)++);        if (sem_post(sem) == -1) {            printf("sem_post parent error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }    }    exit(EXIT_SUCCESS);}

2、Posix共享内存区

mmap函数中的参数fd,可以是open函数打开的一个内存映射文件,还可以是shm_open函数打开的一个共享内存区对象,相关函数如下。

#include <sys/mman.h>int shm_open(const char *name, int oflag, mode_t mode);int shm_unlink(const char *name);

下面是几个简答的例子。

// shmcreate.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <sys/mman.h>#include <sys/stat.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)int main(int argc, char **argv){    int c, fd, flags;    char *ptr;    off_t length;    flags = O_RDWR | O_CREAT;    while ((c = getopt(argc, argv, "e")) != -1) {        switch (c) {        case 'e':            flags |= O_EXCL;            break;        }    }    if (argc - 2 != optind) {        printf("usage: %s [-e] <name> <length>\n", argv[0]);        exit(EXIT_FAILURE);    }    length = atol(argv[optind + 1]);    // sh_open    if ((fd = shm_open(argv[optind], flags, FILE_MODE)) == -1) {        printf("shm_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // ftruncate    if (ftruncate(fd, length) == -1) {        printf("ftruncate error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // mmap    if ((ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    exit(EXIT_SUCCESS);}// shmunlink.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <sys/mman.h>#include <sys/stat.h>int main(int argc, char **argv){    if (2 != argc) {        printf("usage: %s [-e] <name>\n", argv[0]);        exit(EXIT_FAILURE);    }    // sh_unlink    if (shm_unlink(argv[1]) == -1) {        printf("shm_unlink error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    exit(EXIT_SUCCESS);}// shmread.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <sys/mman.h>#include <sys/stat.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)int main(int argc, char **argv){    int i, fd;    struct stat stat;    unsigned char c, *ptr;    if (2 != argc) {        printf("usage: %s <name>\n", argv[0]);        exit(EXIT_FAILURE);    }    // sh_open    if ((fd = shm_open(argv[1], O_RDONLY, FILE_MODE)) == -1) {        printf("shm_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // fstat    if (fstat(fd, &stat) == -1) {        printf("fstat error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // mmap    if ((ptr = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // close    if (close(fd) == -1) {        printf("close error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    for (i = 0; i < stat.st_size; ++i) {        if ((c = *ptr++) != (i % 256)) {            printf("error: ptr[%d] = %d\n", i, c);        }    }    exit(EXIT_SUCCESS);}// shmwrite.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <sys/mman.h>#include <sys/stat.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)int main(int argc, char **argv){    int i, fd;    struct stat stat;    unsigned char *ptr;    if (2 != argc) {        printf("usage: %s <name>\n", argv[0]);        exit(EXIT_FAILURE);    }    // sh_open    if ((fd = shm_open(argv[1], O_RDWR, FILE_MODE)) == -1) {        printf("shm_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // fstat    if (fstat(fd, &stat) == -1) {        printf("fstat error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // mmap    if ((ptr = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // close    if (close(fd) == -1) {        printf("close error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    for (i = 0; i < stat.st_size; ++i) {        *ptr++ = i % 256;    }    exit(EXIT_SUCCESS);}

shm_open创建或打开一个共享内存区对象,创建成功后共享内存区对象的位置在/dev/shm目录下,shm_unlink移除一个共享内存区对象,链接时使用“-lrt”选项。

#include <unistd.h>#include <sys/stat.h>int ftruncate(int fd, off_t length);int fstat(int fd, struct stat *buf);

ftruncate设置一个文件的长度,参数fd为shm_open的返回值,fstat获取一个文件fd的状态。

下面以一个例子说明shm_open等函数的用法。

// server.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <sys/mman.h>#include <sys/stat.h>#include <semaphore.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)struct shmstruct // struct stored in shared memory{    int count;};sem_t *sem; // pointer to named semaphoreint main(int argc, char **argv){    int fd;    struct shmstruct *ptr;    if (3 != argc) {        printf("usage: %s <shm_name> <sem_name>\n", argv[0]);        exit(EXIT_FAILURE);    }    shm_unlink(argv[1]); // unlink if used, OK if failed    // open shm    if ((fd = shm_open(argv[1], O_RDWR | O_CREAT | O_EXCL, FILE_MODE)) == -1) {        printf("shm_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // set size    if (ftruncate(fd, sizeof(struct shmstruct)) == -1) {        printf("ftruncate error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // mmap shm    if ((ptr = mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // close shm    if (close(fd) == -1) {        printf("close error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    sem_unlink(argv[2]); // unlink if used, OK if failed    // open sem    if ((sem = sem_open(argv[2], O_CREAT | O_EXCL, FILE_MODE, 1)) == SEM_FAILED) {        printf("sem_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // close sem    if (sem_close(sem) == -1) {        printf("sem_close error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    exit(EXIT_SUCCESS);}// client.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <sys/mman.h>#include <sys/stat.h>#include <semaphore.h>#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)struct shmstruct // struct stored in shared memory{    int count;};sem_t *sem; // pointer to named semaphoreint main(int argc, char **argv){    int fd, i, nloop;    pid_t pid;    struct shmstruct *ptr;    if (4 != argc) {        printf("usage: %s <shm_name> <sem_name> <#loops>\n", argv[0]);        exit(EXIT_FAILURE);    }    nloop = atoi(argv[3]);    // open shm    if ((fd = shm_open(argv[1], O_RDWR, FILE_MODE)) == -1) {        printf("shm_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // mmap shm    if ((ptr = mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {        printf("mmap error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // close shm    if (close(fd) == -1) {        printf("close error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    // open sem    if ((sem = sem_open(argv[2], 0)) == SEM_FAILED) {        printf("sem_open error: %s\n", strerror(errno));        exit(EXIT_FAILURE);    }    pid = getpid();    for (i = 0; i < nloop; ++i) {        if (sem_wait(sem) == -1) {            printf("sem_wait error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }        printf("pid %ld: %d\n", (long)pid, ptr->count++);        if (sem_post(sem) == -1) {            printf("sem_post error: %s\n", strerror(errno));            exit(EXIT_FAILURE);        }    }    exit(EXIT_SUCCESS);}

例子中,仍然是一个计数程序,不过共享内存区的同步是在没有亲缘关系的进程间完成的。计数变量count放到了shmstruct结构中,同步机制使用Posix信号灯sem。server.c的作用是创建一个共享内存区供其它进程使用,其大小为shmstruct结构体的大小,接着创建一个有名信号灯并初始化为1。client.c的作用是计数,使用server.c已经创建好的共享内存区和信号灯,输出当前进程号和计数值,运行结果如下。

$ gcc -o server server.c -pthread -lrd$ gcc -o client client.c -pthread -lrd$ ./server shm sem$ ./client shm sem 10 & ./client shm sem 10[1] 8423pid 8423: 0pid 8423: 1pid 8423: 2pid 8423: 3pid 8423: 4pid 8423: 5pid 8423: 6pid 8423: 7pid 8423: 8pid 8423: 9pid 8424: 10pid 8424: 11pid 8424: 12pid 8424: 13pid 8424: 14pid 8424: 15pid 8424: 16pid 8424: 17pid 8424: 18pid 8424: 19[1]+  已完成               ./c shm sem 10

可以看出,open与shm_open的用法类似,只不过shm_open创建的共享内存区对象位于虚拟文件系统中,在Linux上的位置是“/dev/shm”,通过file命令查看一下例子中创建的shm的类型,data类型,查看其内容可使用od命令。

$ file /dev/shm/shm/dev/shm/shm: data
1 0