进程间通信之System V 共享内存

文章目录

进程具有独立性,因此进程间通信的前提是两个进程能看到同一份资源

一、共享内存

在内存中申请一块空间,并将起始地址分别映射到两个进程的虚拟地址空间上,便可以让两个进程看到同一份资源

在这里插入图片描述

操作系统为了管理共享内存,除了创建共享内存外,还会创建包含共享内存信息的结构体

系统调用 shmget,头文件 sys/ipc.h、sys/shm.h

// 创建或获取共享内存
int shmget(key_t key, size_t size, int shmflg)

返回值:返回共享内存标识符 (供用户使用),出错返回 -1,并且 errno 被设置为相应的出错信息

参数:

  • key 用于创建或获取共享内存
    由于进程具有独立性,为了让两个进程可以找到同一个共享内存,通信双方的进程约定,创建共享内存的进程将 key 值设置到共享内存对应的结构体中,获取共享内存的进程通过相同的 key 来查找共享内存

  • size 表示共享内存的大小
    操作系统实际申请的共享内存大小为 size 向上调整到 PAGE_SIZE(4KB) 的整数倍

  • shmflag 位图,传递多个标志位时,标志位之间用 | 连接

常用的标志位:

  • IPC_CREAT 表示如果 key 值对应的共享内存不存在则创建,存在则获取
  • IPC_EXCL 不能单独使用,需要和 IPC_CREAT 一起传,表示如果 key 值对应的共享内存不存在则创建,存在则出错,即保证获取的共享内存一定是最新的
  • 0ddd 八进制数,表示共享内存的拥有者、所属组及 other 的操作权限

系统调用 ftok,头文件 sys/types.h、sys/ipc.h,生成创建共享内存时所需要的 key 值

// key_t 类型就是 int 的 typedef
key_t ftok(const char *pathname, int proj_id)

系统调用 shmat / shmdt,头文件 sys/types.h、sys/shm.h

// 将标识符为 shmid 的共享内存与进程虚拟地址 shmaddr 关联,如果 shmaddr 指定 NULL,表示由操作系统自主决定与进程关联的虚拟地址
// shmflg 指定 0 表示以读写方式关联共享内存,指定 SHM_RDONLY 表示以读方式关联共享内存,不存在以写方式关联共享内存,此时进程必须具有共享内存相应的操作权限
// 成功返回与共享内存关联的虚拟地址,失败返回 (void*) -1
void *shmat(int shmid, const void *shmaddr, int shmflg)

// 解除进程虚拟地址 shmaddr 和共享内存的关联
// 成功返回 0,失败返回 -1
int shmdt(const void *shmaddr)

共享内存的生命周期随操作系统,因此创建共享内存的进程退出后,并不会删除共享内存,需要通过系统调用或者指令删除共享内存

系统调用 shmctl,头文件 sys/ipc.h、sys/shm.h

// 对标识符为 shmid 的共享内存执行 cmd 指定的操作,buf 用来设置或者获取共享内存的属性,不需要则指定为 NULL
// cmd 指定 IPC_RMID 表示将共享内存标记为销毁,当共享内存的关联数为 0 时,共享内存会自动销毁
// 成功返回 0,失败返回 -1
int shmctl(int shmid, int cmd, struct shmid_ds *buf)

server 从共享内存中读取,client 向共享内存中写入

// shm.hpp
#ifndef __SHARED_MEMORY__
#define __SHARED_MEMORY__

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>

#define PATHNAME "."
#define PROID 0x666
#define CREATSHM (IPC_CREAT | IPC_EXCL)
#define GETSHM IPC_CREAT
#define CREAT 0 // 创建者
#define GET 1 // 获取者

const size_t gsize = 4096;

namespace starrycat
{
    class SharedMemory
    {
    public:
        SharedMemory(int flag, mode_t mode, size_t size = gsize, const char *pathname = PATHNAME, int proid = PROID)
            : _type((flag == CREATSHM) ? CREAT : GET)
        {
            umask(0);

            // 生成唯一的 key
            key_t key = ftok(pathname, proid);
            if (key < 0)
            {
                std::cout << "key 生成失败: " << errno << " " << strerror(errno) << std::endl;
                exit(1);
            }

            // CREATSHM 表示用 key 值创建共享内存,GETSHM 表示通过 key 值获取共享内存
            _shmid = shmget(key, size, flag | mode);
            if (_shmid < 0)
            {
                std::cout << "共享内存创建失败: " << errno << " " << strerror(errno) << std::endl;
                exit(2);
            }

            // 关联
            _shmaddr = static_cast<char*>(shmat(_shmid, nullptr, 0));
            if (_shmaddr == (char*)-1)
            {
                std::cout << "共享内存关联失败: " << errno << " " << strerror(errno) << std::endl;
                exit(3);
            }
        }

        SharedMemory(const SharedMemory& shm) = delete;
        SharedMemory& operator=(const SharedMemory& shm) = delete;

        ~SharedMemory()
        {
            // 去关联
            shmdt(_shmaddr);

            // 销毁
            if (_type == CREAT) shmctl(_shmid, IPC_RMID, nullptr);
        }

        char* getAddr() { return _shmaddr; }

    private:
        int _type;
        int _shmid;
        char* _shmaddr;
    };
}

#endif

// server.cc
#include "shm.hpp"
#include <iostream>
#include <unistd.h>

using namespace std;

int main()
{
    starrycat::SharedMemory shm(CREATSHM, 0666);
    char* start = shm.getAddr();

    // 通信
    for (int i = 0; i < 5; ++i)
    {
        cout << "从共享内存中读取: " << start << endl;
        sleep(1);
    }

    return 0;
}

// client.cc
#include "shm.hpp"
#include <iostream>
#include <unistd.h>


using namespace std;

int main()
{
    starrycat::SharedMemory shm(GETSHM, 0666);
    char* start = shm.getAddr();

    // 通信
    for (int i = 0; i < 3; ++i)
    {
        start[i] = 'A' + i;
        sleep(1);
    }

    return 0;
}

先启动 myserver,在启动 myclient

在这里插入图片描述

  • ipcs -m
    功能:查看共享内存
  • ipcmk -M 大小 -p 权限
    功能:创建共享内存
  • ipcrm -M/-m key/shmid
    功能:通过 key / shmid 删除共享内存

在这里插入图片描述

共享内存映射到进程的虚拟地址后,进程不需要通过系统调用来读取或写入数据,因此共享内存是所有进程间通信中速度最快的

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值