System V——共享内存

System V

当我们需要不同进程进行通信,首先要让进程看见同一份资源,即物理内存中的———共享内存

共享内存原理图:
在这里插入图片描述

1、创建:申请一块共享内存

2、关联进程:将共享内存映射到进程的地址空间,也就是将共享内存的地址告诉进程。

3、取消关联:未来不想通信时,将进程中的映射取消掉

3、释放共享内存:最后释放共享内存,结束通信

系统中可以用shm(share memory)来进行通信,且同时可以存在多对进程通信,可能存在多个共享内存,所以系统中一定存在很多shm,OS对这些共享内存通过先组织,再描述进行管理。

依此可知,共享内存不只是开辟空间即可,系统也要为了管理shm,构建对应的描述共享内存的结构体对象(伪代码:struct_shm)

共享内存没有保护机制(同步互斥),管道通过接口通信,而共享内存直接通信

共享内存=内存空间+共享内存的数据结构(struct_shm)

创建

在这里插入图片描述

ftok——获取一个共享内存的唯一标识符(key_t)

key_t ftok(const char *pathname, int proj_id);

功能:
获取一个共享内存的唯一标识符key
函数参数:
pathname:可以传入任何文件名
proj_id:项目ID
返回值:
成功返回key值,失败返回-1

shmget——创建共享内存

int shmget(key_t key, size_t size, int shmflg);

功能:
创建共享内存
函数参数:
key:传入ftok函数获取的共享内存唯一标识符
size:共享内存的大小(页:4KB的整数倍),size如果不是整数倍,系统会给你开整数倍,但是实际大小还是你所开的大小
shmflg:权限,由9个权限标准构成
这里介绍两个常用基础权限
IPC_CREAT: 如果底层存在这个标识符的共享内存空间,就打开返回,不存在就创建
IPC_EXCL: 如果底层存在这个标识符的共享内存空间,就出错返回(必须和IPC_CREAT一起使用)
两个选项合起来用就可以创建一个全新的共享内存空间
返回值:
成功返回共享内存标识码值(给用户看的),失败返回-1

shmat——将共享内存空间关联到进程地址空间

void *shmat(int shmid, const void *shmaddr, int shmflg);

功能:
将共享内存空间关联到进程地址空间
参数:
shmid:共享内存标识符(shmat的返回值)
shmaddr:指定连接地址。shmaddr为NULL,核心自动选择一个地址
shmflg:两个可能取值是SHM_RND和SHM_RDONLY
返回值: 成功返回一个指针(虚拟地址空间中共享内存的地址,是一个虚拟地址),失败返回-1(返回值的意思类似于malloc)

shmdt——取消关联

int shmdt(const void *shmaddr);

功能:
取消共享内存空间和进程地址空间的关联
参数:
shmaddr:共享内存的起始地址(shmat获取的指针)
返回值: 成功返回0,失败返回-1

shmctl——控制共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能:
控制共享内存
参数:
shmid:共享内存标识符(shmat的返回值)
cmd:命令,有三个
IPC_STAT: 把shmid_ds结构中设置为共享内存当前关联值
IPC_SET: 把共享内存的当前关联值设置为shmid_ds数据结构中的值
IPC_RMID:删除共享内存段
buf:指向一个报错这共享内存的模式状态和访问权限的数据结构(通常为nullptr)
返回值:
成功返回0,失败返回-1

删除共享内存

1、指令删除ipcrm -m shmid

共享内存的删除操作并非直接删除,而是拒绝后续映射,只有在当前映射链接数为0时,表示没有进程访问了,才会真正被删除

为什么用shmid不用key呢?

key:类比inode,唯一性的区分,只在创建时使用
shmid:类比文件fd,对shm未来所有的操作,在用户层,都用shmid

共享内存的生命周期不随进程结束而消失,是跟随OS的,OS重启才结束生命周期

2、调用系统调用删除

共享内存通信

comm.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__

#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>

using namespace std;

//  IPC_CREAT and IPC_EXCL
// 单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回
// IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
// IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!

#define PATHNAME "."
#define PROJID 0x6666

const int gsize = 4096; //暂时

key_t getKey()
{
    key_t k = ftok(PATHNAME, PROJID);
    if(k == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

// string toHex(int x)
// {
//     char buffer[64];
//     snprintf(buffer, sizeof buffer, "0x%x", x);
//     return buffer;
// }

static int createShmHelper(key_t k, int size, int flag)//封装创建shm
{
    int shmid = shmget(k, gsize, flag);
    if(shmid == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

int createShm(key_t k, int size)//创建shm
{
    umask(0);
    return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);//带上IPC_EXCL,如果存在shm,出错
}

int getShm(key_t k, int size)//获取shm,不带 IPC_EXCL单独一个 IPC_CREAT,存在shm,就返回,不存在就创建。
{
    return createShmHelper(k, size, IPC_CREAT);
}

char* attachShm(int shmid)//关联
{
    char *start = (char*)shmat(shmid, nullptr, 0);
    return start;
}

void detachShm(char *start)//取消关联
{
    int n = shmdt(start);//取消关联
    assert(n != -1);
    (void)n;
}

void delShm(int shmid)//删除shm空间
{
    int n = shmctl(shmid, IPC_RMID, nullptr);//IPC_RMID删除共享内存段
    assert(n != -1);
    (void)n;
}

#define SERVER 1
#define CLIENT 0

class Init
{
public:
    Init(int t):type(t)//依据传值初始化 客户端/服务端
    {
        key_t k = getKey();
        if(type == SERVER) shmid = createShm(k, gsize);//如果是服务端,创建shm
        else shmid = getShm(k, gsize);//如果是客户端,获取shm
        start = attachShm(shmid);//把shm映射到客户端/服务端中的进程地址空间
    }
    char *getStart(){ return start; }
    ~Init()
    {
        detachShm(start);//取消关联,从进程地址空间中删除映射
        if(type == SERVER) delShm(shmid);//由服务端删除shm
    }
private:
    char *start;
    int type; //server or client
    int shmid;
};

#endif

server.cc

#include "comm.hpp"
#include <unistd.h>

int main()
{
    Init init(SERVER);
    char *start = init.getStart();

    int n = 0;
    while(n <= 30)
    {
        cout <<"client -> server# "<< start << endl;
        sleep(1);
        n++;
    }
    return 0;
}

client.cc

#include "comm.hpp"
#include <unistd.h>

int main()
{
    Init init(CLIENT);
    char *start = init.getStart();
    char c = 'A';

    while(c <= 'Z')
    {
        start[c - 'A'] = c;
        c++;
        start[c - 'A'] = '\0';
        sleep(1);
    }

    return 0;
}

在这里插入图片描述

ipcs -m 查看共享内存信息

perms是权限

bytes是共享空间大小

nattch关联这个共享内存的进程个数

Tips:如果bytes大小给了4097 用户用的是4097 底层OS分配还是对齐4KB给8KB,page页4KB对齐

在这里插入图片描述
在这里插入图片描述

共享内存的优点

在通信时,没有使用任何接口,一旦共享内存映射到进程地址空间,该共享内存就被所有进程直接看到,因为共享内存这种特性,可以在通信时减少拷贝次数,所以共享内存是所有进程间通信最快的。
在这里插入图片描述
在这里插入图片描述

消息队列

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值