Posix共享内存区与mmap内存映射

本文介绍了共享内存的概念及其在进程间通信中的应用,详细解释了mmap函数的使用方法,包括参数说明及实例演示。此外,还介绍了Posix共享内存区的相关函数,并通过实例展示了如何创建和使用共享内存。

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

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 10
parent: 0
parent: 1
parent: 2
parent: 3
parent: 4
parent: 5
parent: 6
parent: 7
parent: 8
parent: 9
child: 0
child: 1
child: 2
child: 3
child: 4
child: 5
child: 6
child: 7
child: 8
child: 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 10
parent: 0
parent: 1
child: 2
child: 3
child: 4
child: 5
child: 6
child: 7
child: 8
child: 9
child: 10
child: 11
parent: 12
parent: 13
parent: 14
parent: 15
parent: 16
parent: 17
parent: 18
parent: 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 semaphore

int 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 semaphore

int 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] 8423
pid 8423: 0
pid 8423: 1
pid 8423: 2
pid 8423: 3
pid 8423: 4
pid 8423: 5
pid 8423: 6
pid 8423: 7
pid 8423: 8
pid 8423: 9
pid 8424: 10
pid 8424: 11
pid 8424: 12
pid 8424: 13
pid 8424: 14
pid 8424: 15
pid 8424: 16
pid 8424: 17
pid 8424: 18
pid 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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值