关于Posix共享内存

本文详细介绍了POSIX内存映射技术在无亲缘关系进程间共享内存区的两种方法:内存映射文件和共享内存区对象,并通过实例展示了如何在服务端和客户端之间进行消息传递。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Posix提供了两种在无亲缘关系的进程间共享内存区的方法:
    1. 内存映射文件

            由open函数打开, 由mmap函数把得到的描述符映射到当前进程的地址空间的一个文件
    2. 共享内存区对象
            由shm_open打开一个Posix IPC名字, mmap将得到的描述符映射到当前进程的地址空间   

两者的差距在于获取描述符的手段不同




操作函数:
int shm_open(const char *name, int oflag, mode_t mode);
shm_open与sem_open, mq_open不同的地方在于, mode总是要指定的. 如果没有指定O_CREAT, 那么mode可以是0

int shm_unlink(const char *name);
shm_unlink与其他unlink函数一样, 直到全部引用关闭才删除. 其中一个作用是防止后续的open调用成功


以下给出使用的实例:
         
//头文件
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */

#define     MESGSIZE    256        //every message's max size
#define     NMESG        16        //total max size

struct shm_ds{
    sem_t     ds_mutex;
    sem_t    ds_nempty;
    sem_t    ds_nstored;
    int     ds_nput;
    long     ds_noverflow;            //如果有客户没有空间再存放进共享内存, 那么这个变量加一. 该客户不再等待
    sem_t     ds_overflowmutex;        //这变量也是共享的, 需要加锁
    long     ds_msgoff[NMESG];        //每个消息的起始位置
    char    ds_msgbuf[NMESG * MESGSIZE];    //所有消息的占有空间
};


//服务端代码
#include "scpm.h"

int main(int ac, char *av[])
{
    int fd, index;
    int over_flows, offset, temp;
    struct shm_ds *ptr;

    if(ac != 2){
        fprintf(stderr, "Usage server <pathname>");
        exit(-1);
    }

    shm_unlink(av[1]);            //保证我们创建的共享内存是新的
    fd = shm_open(av[1], O_RDWR | O_CREAT | O_EXCL, (S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH));
    if(fd < 0){
        perror("shm_open error:");
        exit(-1);
    }
    ftruncate(fd, sizeof(struct shm_ds));    //调整对象的大小

    ptr = mmap(NULL, sizeof(struct shm_ds), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(ptr == MAP_FAILED){
        perror("mmap error");
        exit(-1);
    }
    close(fd);

    for(index=0; index<NMESG; index++)        //初始化每个消息的起始位置, 为了方便以后使用
        ptr->ds_msgoff[index] = index * MESGSIZE;
    
    sem_init(&ptr->ds_mutex, 1, 1);        //每个共享变量都是shared的
    sem_init(&ptr->ds_nempty, 1, NMESG);
    sem_init(&ptr->ds_nstored, 1, 0);
    sem_init(&ptr->ds_overflowmutex, 1, 1);

    index = 0;
    over_flows = 0;
    for(;;)                        //下面即为多个生产者一个消费者的模型
    {
        sem_wait(&ptr->ds_nstored);
        sem_wait(&ptr->ds_mutex);
        offset = ptr->ds_msgoff[index];
        printf("Index : %d . Content is [%s]\n", index, &ptr->ds_msgbuf[offset]);
        if(++index >= NMESG)
            index = 0;
        sem_post(&ptr->ds_mutex);
        sem_post(&ptr->ds_nempty);

        sem_wait(&ptr->ds_overflowmutex);
        temp = ptr->ds_noverflow;
        sem_post(&ptr->ds_overflowmutex);
        if(temp != over_flows)            //因为没有位置而被放弃的客户个数,发生变化则输出告知
        {
            printf("now %d are abandoned\n", temp);
            over_flows = temp;
        }

    }
    return 0;
}



//客户端代码
#include "scpm.h"

int main(int ac, char *av[])
{
    int loops, nusec;
    int fd, index, offset;
    pid_t pid;
    struct shm_ds *ptr;
    char buf[MESGSIZE];

    if(ac != 4){
        fprintf(stderr, "Usage : cspm <pathname> loops usec\n");
        exit(-1);
    }

    loops = atoi(av[2]);
    nusec = atoi(av[3]);

    fd = shm_open(av[1], O_RDWR, 0);
    ptr = mmap(NULL, sizeof(struct shm_ds), PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
    close(fd);

    pid = getpid();
    //这里我们让一个客户端多次输出, 模拟多个客户端的情况
    for(index = 0; index < loops; index++)
    {
        usleep(nusec);
        snprintf(buf, MESGSIZE, "pid : %ld : message : %d", pid, index);    //消息内容

        //这里我们使用trywait的原因是某些客户端比如HTTP不会愿意等待, 如果没有消息存放位置则选择直接返回
        if(sem_trywait(&ptr->ds_nempty) == -1){        
            if(errno == EAGAIN){
                sem_wait(&ptr->ds_overflowmutex);
                ptr->ds_noverflow ++;
                sem_post(&ptr->ds_overflowmutex);
                continue;
            }
            else{
                perror("sem_trywait error");
                exit(-1);
            }
        }
        sem_wait(&ptr->ds_mutex);
        offset = ptr->ds_msgoff[ptr->ds_nput];
        if(++(ptr->ds_nput) >= NMESG)    
            ptr->ds_nput = 0;
        sem_post(&ptr->ds_mutex);            //这里我们让互斥锁之间的critical code尽量的少, 以免互斥锁会影响性能
        strcpy(&ptr->ds_msgbuf[offset], buf);
        sem_post(&ptr->ds_nstored);
    }

    return 0;
}


接下来, 就看一下程序的运行结果:
$ gcc -o scpm server_clients_posix_mmap.c -lrt    //服务端
$ gcc -o cspm clients_server_posix_mmap.c -lrt    //客户端
$ ./scpm /haha &
$ ./cspm /haha 5 0
$ ./scpm /haha
Index : 0 . Content is [pid : 5911 : message : 0]        //下面即为后台服务器的输出
Index : 1 . Content is [pid : 5911 : message : 1]
Index : 2 . Content is [pid : 5911 : message : 2]
Index : 3 . Content is [pid : 5911 : message : 3]
Index : 4 . Content is [pid : 5911 : message : 4]
^C

接下来不会再介绍System V的共享内存, 因为两者相差无几, 只是调用的函数换了换
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值