[Linux]进程间通信--共享内存

[Linux]system V共享内存


共享内存是Linux系统下的一种进程间通信手段,其符合system V标准。System V标准下的通信手段接口在使用方法上是相似的,包括信号量、共享内存和消息队列这三种通信机制,

共享内存通信的原理

共享内存是操作系统在内存中申请的一块空间,进程将该内存映射到自身进程地址空间的共享区,然后通过这个映射往共享内存写入或读取数据,完成进程间的通信。

image-20230910185150016

完成通信后,进程要先解除映射关系,然后让操作系统释放共享内存。

系统接口

创建共享内存接口

Linux系统中提供了shmget接口用于创建共享内存:

//shmget所在的头文件和声明
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
  • 成功返回共享内存的编号shmid,失败返回-1,错误码被设置。

key参数

共享内存是操作系统在内存中申请的一块内存空间,操作系统中可能会有大量的共享内存,操作系统为了管理这些共享内存就要用相应的结构进行描述,然后将描述的结构组织起来,共享内存中存在着一个字段用于唯一标识共享内存,这个字段使用的就是key参数传入的值。在进程使用共享内存通信前,一个进程创建共享内存并将key参数写入,然后另一个想要与其通信的进程就用同样的key参数和操作系统中的共享内存描述结构中的key参数进行比对,找到对应的共享内存,从而进行通信。

image-20230910193529432

为了让使用的key参数唯一,保证进程使用的共享内存不会出错,我们使用ftok系统接口:

//ftok所在的头文件和声明
#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
  • ftok会根据传入的参数使用算法返回一个重复率极低的数字。
  • 成功返回0,失败返回-1,错误码被设置。

size参数

size参数用于指明要创建的共享内存的大小,单位为字节。

操作系统创建共享内存是以page页为单位的,大小为4KB。

shmflg参数

shmflg参数用于指明shmget的使用模式。

  • IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回。
  • IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
  • IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 – 如果创建成功,对应的shm,一定是最新的!
  • 传入umask可以给要创建的共享内存设置权限。

关联共享内存接口

要想使用共享内存必须进行共享内存的关联,将共享内存映射到自己的进程地址空间。Linux系统中提供了shmat接口用于关联共享内存:

//shmat所在的头文件和声明
#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);
  • shmid参数 – 用于指明要关联的共享内存编号。
  • shmaddr参数 – 指明要关联到的地址处,传入空指针操作系统会自己进行关联。
  • shmflg参数 – 指明对要关联的共享内存的权限,传入0为读写权限。
  • 成功返回共享内存映射在进程地址空间中的地址,失败返回-1,错误码被设置。

去关联共享内存接口

在使用完共享内存进行通信后,要进行共享内存的去关联操作。Linux系统中提供了shmdt接口用于去关联共享内存:

//shmdt所在的头文件和声明
#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);
  • shmaddr参数 – 要去关联的共享内存的地址,即调用shmat关联共享内存时返回的地址。
  • 成功返回0,失败返回-1,错误码被设置。

删除共享内存接口

Linux系统中提供了shmctl接口用于控制共享内存:

//shmctl所在的头文件和声明
#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • shmid参数 – 用于指明要控制的共享内存编号。
  • cmd参数 – 用于指定控制选项,其中IPC_RMID选项就是删除共享内存。
  • buf参数 – 输出型参数,用于接收描述共享内存的结构体信息。

使用指令操作共享内存

查看共享内存

在Linux系统中使用ipcs -m可以查看系统中的共享内存:

image-20230910203751843

  • key:进程调用shmget创建共享内存时传入的key参数
  • shmid:共享内存编号。
  • owner:共享内存的拥有者。
  • perms:拥有者对共享内存的权限。
  • bytes:共享内存的大小。

删除共享内存

在Linux系统中使用ipcrm -m 对应shmid可以删除对应的共享内存:

image-20230910204051386

共享内存的特性

  • 无需多余拷贝:使用共享内存通信不需要使用任何接口,只要共享内存被映射到进程的地址空间中,进程就能看到共享内存。
  • 速度快 :共享内存被映射到进程的地址空间中,进程就能看到共享内存,不涉及缓冲区,无需多余拷贝动作,因此共享内存通信速度很快。
  • 无保护:使用共享内存通信不需要使用任何接口,因此共享内存不存在任何保护机制。

编码测试共享内存

在编码测试共享内存前,创建三个文件:common.hppserver.ccclient.cc

common.hpp用于实现使用共享内存的类和接口,具体内容如下:

#ifndef __COMN_HPP__
#define __COMM_HPP__

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

using namespace std;

#define PATHNAME "."
#define PROJID 0x6666

#define gsize 4096

//key_t类型实际也是int类型
key_t GetKey()//获取key值
{
    key_t k = ftok(PATHNAME, PROJID);
    if(k == -1)//错误检测
    {
        cerr << "errno: " << errno << " strerror: " << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

//只在本文件生效
static int createShmHelper(key_t key, size_t size, int shmflg)
{
    int shmid = shmget(key, size, shmflg);
    if(shmid == -1)//错误检测
    {
        cerr << "errno: " << errno << " strerror: " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

int createShm(key_t k, int size)
{
    umask(0);
    return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);
}

int getShm(key_t k, int size)
{
    return createShmHelper(k, size, IPC_CREAT);
}

void* attachShm(int shmid)
{
    void* start = shmat(shmid, NULL, 0);
    return start;
}

void detachShm(void* start)
{
    int n = shmdt(start);
    assert(n != -1);
    (void)n;
}

void delShm(int shmid)
{
    int n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;
}

#define SERVER 1
#define CLIENT 0    

class  Shm
{
public:
    Shm(int type):_type(type)
    {
        key_t key = GetKey();
        if (_type == SERVER) _shmid = createShm(key, gsize);
        else _shmid = getShm(key, gsize);
        _start = attachShm(_shmid);
    }
    void* getStart()
    {
        return _start;
    }
    ~Shm()
    {
        detachShm(_start);
        if (_type == SERVER) delShm(_shmid);
    }
private:
    void* _start;
    int _shmid;
    int _type;
};

#endif

server.cc用于创建共享内存,读取共享内存中的数据,完成共享内存的释放,具体内容如下:

#include "common.hpp"

int main()
{
    Shm shm(SERVER);
    char* start = (char*)shm.getStart();

    int n = 0;
    while(n < 30)
    {
        cout << "client send me : " << start << endl;
        n++;
        sleep(1);
    }
    return 0;
}

client.cc用于向共享内存写入数据,具体内容如下:

#include "common.hpp"

int main()
{
    Shm shm(CLIENT);
    char* start = (char*)shm.getStart();

    char ch = 'a';
    while(ch <= 'z')
    {
        start[ch - 'a'] = ch;
        ch++;
        start[ch - 'a'] = 0;
        sleep(1);
    }
    return 0;
}

编译代码运行并查看结果:

共享内存

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好想写博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值