文章目录
共享内存基本概念
共享内存是目前比较主流的进程之间互相通信的方式,这种通信中读取数据的对象和写入数据的对象不一定是同一个,写入的所有数据放在一块共享的内存中,这样方式的通信十分方便快捷。但是这样就会有一些数据的同步问题,因为在这个内存上的读取时没有阻塞的,每个人都不知道自己读取的数据是不是最新的自己需要的数据,这也是我们在使用共享内存进行通信的时候需要注意的问题。
共享内存可以将一个文件映射到一块内存上,这种不同于以往读取文件内容的方式,我们使用管道或者其他方式读写文件内容时,往往需要打开两次文件,一次读取文件,将文件内容读入一个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 | 失败 |
利用文件映射的方式读文件内容
基本思路:
- 打开一个文件
- 进行映射,将文件映射进内存
- 对文件内容进行操作
- 结束文件映射
因为我们只是读取文件内容,所以在打开文件open时使用只读标志O_RDONLY
,且映射mmap时,内存保护标志为PROT_READ
(内存是用来读还是用来写的)。
在main的参数中,我们使用了argc和argv这两个参数,具体解释如下:
argv
和argc
指的是终端运行该可执行文件时的参数,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所指向的内存内容进行修改。
修改结束后就将内存映射回文件中,结束映射。
我们总结一下基本思路:
- 打开文件
- 对文件内容进行扩展
- 映射文件,得到映射的内存地址buff
- 对buff指定的文件内容进行修改
- 将文件映射回内存,结束映射。
#include <iostream>
#include <unistd.h>
#