【进程间通信2】使用共享内存实现进程间的通信(附C++实现代码)


共享内存基本概念

共享内存是目前比较主流的进程之间互相通信的方式,这种通信中读取数据的对象和写入数据的对象不一定是同一个,写入的所有数据放在一块共享的内存中,这样方式的通信十分方便快捷。但是这样就会有一些数据的同步问题,因为在这个内存上的读取时没有阻塞的,每个人都不知道自己读取的数据是不是最新的自己需要的数据,这也是我们在使用共享内存进行通信的时候需要注意的问题。

共享内存可以将一个文件映射到一块内存上,这种不同于以往读取文件内容的方式,我们使用管道或者其他方式读写文件内容时,往往需要打开两次文件,一次读取文件,将文件内容读入一个buff中,然后在buff中进行修改,再将buff中的内容重新写回去。而共享内存可以直接在内存上进行修改最终映射回去,它相比而言效率更高。
在这里插入图片描述

内存映射文件

相关函数总结

函数功能 函数格式
创建共享内存 int shm_open(const char *name, int oflag, mode_t mode)
取消内存共享 int shm_unlink(const char *name)
建立内存映射 void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset)
关闭内存映射 int munmap(void *start,size_t length)

接下来我们将详细讲解以上这些函数的用法。

创建共享内存

函数定义如下:
int shm_open(const char *name, int oflag, mode_t mode)

其中的参数具体如下:

参数 含义
name posix IPC名字,格式为/somename
oflag 标志
mode 权限

具体的标志有:

标志名 含义
O_CREAT 没有该对象则创建
O_EXCL 如果O_CREAT指定,但name不存在,就返回错误
O_RDONLY 只读
O_RDWR 读写
O_TRUNC 若存在则截断

具体的权限有:

权限 含义
S_IWUSR 用户/属主写
S_IRUSR 用户/属主读
S_IWGRP 组成员写
S_IRGRP 组成员读
S_IWOTH 其他用户写
S_IROTH 其他用户读

返回值的含义为:

返回值
-1 出错
其他 共享内存描述符

取消内存共享

函数定义如下:
int shm_unlink(const char *name)

具体参数如下:

参数 含义
name posix共享内存名字

返回值如下:

返回值 含义
-1 出错
0 成功

建立映射函数

函数定义如下:
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset)

对于映射函数mmap的具体参数意义如下:

参数 含义
start 映射区的开始地址,通常使用NULL,让系统决定映射区的起始地址
length 映射区的长度,单位字节,不足一内存页按一内存页处理
prot 内存保护标志
flags 映射对象的类型
fd 文件描述符,不能是套接字和终端的fd,-1为匿名内存映射
offset 被映射对象内容的起点,只能是页大小的整数倍

其中关于内存的保护标志:

参数 含义
PROT_EXEC 页内容可以被执行
PROT_READ 页内容可以被读取
PROT_WRITE 页可以被写入
PROT_NONE 页不可访问,不能与文件的打开模式冲突

关于映射对象类型:

参数 含义
MAP_SHARED 变动共享
MAP_PRIVATE 变动私有
MAP_ANON 匿名内存映射

关于返回值:本质上是映射的内存地址

MAP_FAILED 映射失败
非MAP_FAILED 映射成功,返回共享内存地址

结束映射函数

函数定义如下:
int munmap(void *start,size_t length)

参数意义:

参数 含义
start 映射内存起始地址
length 内存大小

返回值意义:

返回值 含义
0 成功
-1 失败

利用文件映射的方式读文件内容

基本思路:

  1. 打开一个文件
  2. 进行映射,将文件映射进内存
  3. 对文件内容进行操作
  4. 结束文件映射

因为我们只是读取文件内容,所以在打开文件open时使用只读标志O_RDONLY,且映射mmap时,内存保护标志为PROT_READ(内存是用来读还是用来写的)。

在main的参数中,我们使用了argc和argv这两个参数,具体解释如下:

argvargc指的是终端运行该可执行文件时的参数,argc值的是参数个数,argv指的是终端执行命令时的字符串数组。如./a.out test,此时的argc=2,argv={./a.out, test}

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <cstring>
using namespace std;

int main(int argc, char** argv){
   
    if(3!=argc){
    // 打开文件时防止出错
        printf("Usage:%s filepath size\n",argv[0]);
        return 1;
    }
    // 打开文件
    int fd = open(argv[1],O_RDONLY);
    // 进行映射  
    void* buff = mmap(NULL,stoi(argv[2]),PROT_READ,MAP_SHARED,fd,0);   // 进行映射
    if(buff == MAP_FAILED){
      // 测试文件是否映射失败
        perror("mmap error");
        return 1;
    }
    cout << static_cast<char*>(buff) << endl;  // 显示被映射的文件内容
    munmap(buff,stoi(argv[2]));  // 将文件映射回去
    close(fd);
}

以上代码就是将在终端给出的文件内容映射进buff中

利用文件映射的方式写文件内容

这里需要注意的是,我们的时候,同时还需要读文件,所以在打开文件的时候,open函数中的参数为O_RDWR,在mmap函数中的保护参数为PROT_READ|PROT_WRITE

在打开文件之后,我们需要对文件映射的内存进行扩展,防止在文件大小过小,无法成功修改。

我们将文件映射后将会得到一个内存地址buff,所以我们想要更改文件内容,就是在buff上直接对数据进行修改即可,在给出的代码中我们直接使用strcpy对buff所指向的内存内容进行修改。

修改结束后就将内存映射回文件中,结束映射。

我们总结一下基本思路:

  1. 打开文件
  2. 对文件内容进行扩展
  3. 映射文件,得到映射的内存地址buff
  4. 对buff指定的文件内容进行修改
  5. 将文件映射回内存,结束映射。
#include <iostream>
#include <unistd.h>
#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值