共享内存基本概念内存映射文件
相关函数利用文件映射的方式读文件内容利用文件映射的方式写文件内容利用共享内存直接创建文件 使用共享内存进行进程间通信创建可命名的共享内存
共享内存基本概念
共享内存是目前比较主流的进程之间互相通信的方式,这种通信中读取数据的对象和写入数据的对象不一定是同一个,写入的所有数据放在一块共享的内存中,这样方式的通信十分方便快捷。但是这样就会有一些数据的同步问题,因为在这个内存上的读取时没有阻塞的,每个人都不知道自己读取的数据是不是最新的自己需要的数据,这也是我们在使用共享内存进行通信的时候需要注意的问题。
共享内存可以将将一个文件映射到一块内存上;这种不同于以往读取文件内容的方式,我们使用管道或者其他方式读写文件内容时,往往需要打开两次文件,一次读取文件,将文件内容读入一个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) |
利用文件映射的方式读文件内容这里是引用
argv和argc指的是终端运行该可执行文件时的参数,argc值的是参数个数,argv指的是终端执行命令时的字符串数组。如./a.out test,此时的argc=2,argv={./a.out, test}。
#include#include #include #include #include using namespace std; int main(int argc, char** argv){ if(3!=argc){ printf("Usage:%s filepath sizen",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 (buff) << endl; // 显示被映射的文件内容 munmap(buff,stoi(argv[2])); // 将文件映射回去 close(fd); }
利用文件映射的方式写文件内容以上代码就是将在终端给出的文件内容映射进buff中
#include#include #include #include #include using namespace std; // ./a.out filepath size int main(int argc, char** argv){ if(3!=argc){ printf("Usage:%s filepath sizen",argv[0]); return 1; } int fd = open(argv[1],O_RDWR); void* buff = mmap(NULL,stoi(argv[2]),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(buff == NULL){ perror("mmap error"); return 1; } cout << static_cast (buff) << endl; int pos; string s; cin >> pos >> s; strcpy(static_cast (buff)+pos,s.c_str()); // 对映射的内存内容的pos位置增添内容s munmap(buff,stoi(argv[2])); close(fd); }
利用共享内存直接创建文件我们主要是想向读者表达共享内存可以直接在映射的内存上对文件进行修改。该代码就是在映射文件内容中增添pos的内容。
#include#include #include #include #include using namespace std; // ./a.out filepath size int main(int argc, char** argv){ if(3!=argc){ printf("Usage:%s filepath sizen",argv[0]); return 1; } int fd = open(argv[1],O_RDWR|O_CREAT,0666); // 如果文件存在就打开该文件,如果文件不存在就创建一个权限为0666的文件 ftruncate(fd,stoi(argv[2])); // 因为我们直接使用open函数创建的文件大小为0,所以在对该文件进行映射前需要使用该函数对文件大小进行扩充。 void* buff = mmap(NULL,stoi(argv[2]),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(buff == NULL){ perror("mmap error"); return 1; } string s; cin >> s; strcpy(static_cast (buff),s.c_str()); // 对映射的内存内容增添内容s munmap(buff,stoi(argv[2])); close(fd); }
使用共享内存进行进程间通信使用共享内存创建文件一定要注意在对文件内容进行映射之前,需要扩充文件大小。
我们首先创建两个进程,工作代码如下:
#include#include #include #include using namespace std; // 共享内存:让父子进程共享内存 int main(){ int n = 0; if(fork() == 0){ for(int i=0; i<10; i++) cout << getpid() << ":" << ++n << endl; }else{ for(int i=0; i<10; i++) cout << getpid() << ":" << --n << endl; } }
运行结果如下:
我们可以看出来这两个进程独自运行,互不干扰。
接下来,我们创建一个共享内容,让该父子进程同时访问该内存上的内容。
创建步骤:
打开/dev/zero文件使用mmap函数进行映射,创建共享内存
代码如下:
#include#include #include #include using namespace std; // 共享内存:让父子进程共享内存 int main(){ int fd = open("/dev/zero",O_RDWR); // /dev/zero创建共享内存的一个特 殊文件 if(-1 == fd){ // 判断文件是否打开成功 perror("open error"); return 1; } void* buff = mmap(NULL,sizeof(int),PROT_WRITE|PROT_READ,MAP_SHARED,fd,0); if(MAP_FAILED == buff){ // 判断文件是否映射成功 perror("mmap error"); return 1; } int &n = *static_cast (buff); if(fork() == 0){ for(int i=0; i<10; i++) cout << getpid() << ":" << ++n << endl; }else{ for(int i=0; i<10; i++) cout << getpid() << ":" << --n << endl; } munmap(buff, sizeof(int)); buff = NULL; close(fd); }
运行结果如下:
从运行结果我们就可以看出该父子进程是在同一个内存上进行的。
除了以上这种创建共享内存之外,我们还可以使用另外一种风格,这两种方法效果相同,主要区别是不需要打开文件,直接创建一个匿名共享内存,创建方法相对简单,但是这种匿名共享内存只能在亲缘进程中使用。具体代码如下:
#include#include #include #include using namespace std; // 共享内存:让父子进程共享内存 int main(){ void* buff = mmap(NULL,sizeof(int),PROT_WRITE|PROT_READ,MAP_SHARED|MAP_ANON,-1,0); // 申请匿名共享内存 if(MAP_FAILED == buff){ // 判断文件是否映射成功 perror("mmap error"); return 1; } int &n = *static_cast (buff); if(fork() == 0){ for(int i=0; i<10; i++) cout << getpid() << ":" << ++n << endl; }else{ for(int i=0; i<10; i++) cout << getpid() << ":" << --n << endl; } munmap(buff, sizeof(int)); buff = NULL; }
执行结果如下:
创建可命名的共享内存我们知道匿名的共享内存只能父子进程才能使用,那么非亲缘进程该怎么使用共享内存呢?
这就用到了我们开头提及的shm_open函数,在这一节我们会写三个文档,分别用于创建共享内存、向共享内存中写数据以及读出共享内存中的数据。具体如下:
create_shared_memory.cpp:创建共享内存
该文件内容如下:
#include#include #include #include using namespace std; int main(int argc, char* argv[]){ if(3 != argc){ printf("Usage:%s name sizen",argv[0]); return 1; } int fd = shm_open(argv[1],O_CREAT|O_RDWR,0666); // 打开一个共享内存 if(-1 == fd){ perror("shm_open error"); return 1; } ftruncate(fd,stoi(argv[2])); close(fd); }
然后我们在终端链接库执行g++ create_shared_memory.cpp -lrt运行程序,然后再做以下命令:
我们发现该文件创建后默认在dev/shm文件夹下。
shm_write.cpp:使用共享内存写数据
文件内容如下:
#include#include #include #include using namespace std; int main(int argc, char* argv[]){ int fd = shm_open(argv[1], O_RDWR, 0); if(-1 == fd){ perror("shm_open error"); return 1; } void* buff = mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); if(MAP_FAILED == buff){ perror("mmap error"); return 1; } cin >> *(int*)buff; munmap(buff, sizeof(int)); close(fd); }
我们在终端执行如下命令:
需要注意的是,执行命令./a.out /abc中的/abc并不是指根目录下的abc文件,而是指使用shm_open创建的文件的默认路径下。
shm_read.cpp:使用共享内存读数据
文件内容如下:
#include#include #include #include using namespace std; int main(int argc, char* argv[]){ int fd = shm_open(argv[1], O_RDONLY, 0); if(-1 == fd){ perror("shm_open error"); return 1; } void* buff = mmap(NULL, sizeof(int), PROT_READ, MAP_SHARED, fd, 0); if(MAP_FAILED == buff){ perror("mmap error"); return 1; } cout << *(int*)buff << endl; munmap(buff, sizeof(int)); close(fd); }
终端执行命令如下:



