共享內存是一種進程間通信的方式,速度比較快
基本原理:以頁面爲單位,將一個普通文件映射到內存中,達到共享內存和節約內存的目的,通常在需要對文件進行頻繁讀寫時使用,這樣用內存讀寫取代I/O讀寫,以獲得較高的性能
windows和linux都提供了原生的系統級的C++接口,可以將文件映射到內存
- windows中使用CreateFileMapping
- linux使用mmap
代碼示例
這裏實現了兩個進程,writer進程往共享內存裏寫數據,reader進程從共享內存裏讀數據
main_writer.cpp
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#ifdef _WIN32
#include <windows.h>
#elif __linux
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#endif
using namespace std;
#ifdef _WIN32
struct MyData
{
string name;
int age;
MyData(string _name, int _age) : name(_name), age(_age)
{}
};
void writeMemory()
{
// define shared data
char *shared_file_name = "my_shared_memory";
unsigned long buff_size = 4096;
char share_buffer[] = "greetings, hello world";
//MyData share_buffer("Tom", 18);
// create shared memory file
HANDLE dump_file_descriptor = CreateFile(shared_file_name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS, // open exist or create new, overwrite file
FILE_ATTRIBUTE_NORMAL,
NULL);
if (dump_file_descriptor == INVALID_HANDLE_VALUE)
cout << "create file error" << endl;
HANDLE shared_file_handler = CreateFileMapping(
dump_file_descriptor, // Use paging file - shared memory
NULL, // Default security attributes
PAGE_READWRITE, // Allow read and write access
0, // High-order DWORD of file mapping max size
buff_size, // Low-order DWORD of file mapping max size
shared_file_name); // Name of the file mapping object
if (shared_file_handler)
{
// map memory file view, get pointer to the shared memory
LPVOID lp_base = MapViewOfFile(
shared_file_handler, // Handle of the map object
FILE_MAP_ALL_ACCESS, // Read and write access
0, // High-order DWORD of the file offset
0, // Low-order DWORD of the file offset
buff_size); // The number of bytes to map to view
// copy data to shared memory
memcpy(lp_base, &share_buffer, sizeof(share_buffer));
FlushViewOfFile(lp_base, buff_size); // can choose save to file or not
// process wait here for other task to read data
cout << "already write to shared memory, wait ..." << endl;
//cout << share_buffer << endl;
this_thread::sleep_for(chrono::seconds(10));
// close shared memory file
UnmapViewOfFile(lp_base);
CloseHandle(shared_file_handler);
CloseHandle(dump_file_descriptor);
//unlink(shared_file_name);
cout << "shared memory closed" << endl;
}
else
cout << "create mapping file error" << endl;
}
#elif __linux
struct MyData
{
char name[20];
int age;
};
void writeMemory()
{
// specify shared file path
char *shared_file_name = "/home/user/codetest/my_shared_memory";
// define shared data
// unsigned long buff_size = 4096;
// char share_buffer[] = "greetings, hello world";
// MyData share_buffer("Tom", 18);
MyData share_buffer = { "Tom", 18 };
// create mmap file
int fd = open(shared_file_name, O_CREAT | O_RDWR | O_TRUNC, 00777);
if (fd < 0)
cout << "create file error" << endl;
size_t write_size = sizeof(share_buffer);
ftruncate(fd, write_size); // extend file size
// map memory to file
void *p = mmap(NULL, write_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// copy data to shared memory
memcpy(p, &share_buffer, write_size);
cout << "already write to shared memory, wait ..." << endl;
//cout << share_buffer << endl;
this_thread::sleep_for(chrono::seconds(10));
// unmap and close
munmap(p, write_size);
close(fd);
}
#endif
int main()
{
writeMemory();
return 0;
}
main_reader.cpp
#include <iostream>
#include <string>
#ifdef _WIN32
#include <windows.h>
#elif __linux
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#endif
using namespace std;
#ifdef _WIN32
struct MyData
{
string name;
int age;
MyData(string _name, int _age) : name(_name), age(_age)
{}
};
void readMemory()
{
char *shared_file_name = "my_shared_memory";
// open shared memory file
HANDLE shared_file_handler = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
NULL,
shared_file_name);
if (shared_file_handler)
{
LPVOID lp_base = MapViewOfFile(
shared_file_handler,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
// copy shared data from memory
cout << "read shared data: " << endl;
const unsigned long buff_size = 4096;
//char share_buffer[buff_size] = { 0 };
//strcpy(share_buffer, (char *)lp_base);
char *share_buffer = (char *)lp_base;
cout << share_buffer << endl;
/*MyData *my_data = (MyData *)lp_base;
cout << my_data->name << " " << my_data->age << endl;*/
// close share memory file
UnmapViewOfFile(lp_base);
CloseHandle(shared_file_handler);
}
else
cout << "open mapping file error" << endl;
}
#elif __linux
struct MyData
{
char name[20];
int age;
};
void readMemory()
{
// specify shared file path
char *shared_file_name = "/home/user/codetest/my_shared_memory";
// open mmap file
int fd = open(shared_file_name, O_RDONLY, 00777);
if (fd < 0)
cout << "open file error" << endl;
const unsigned long buff_size = 4096;
// size_t read_size = buff_size;
size_t read_size = sizeof(MyData);
// map file to memory
void *p = mmap(NULL, read_size, PROT_READ, MAP_SHARED, fd, 0);
cout << "read shared data: " << endl;
// char *share_buffer = (char *)p;
// cout << share_buffer << endl;
MyData *share_buffer = (MyData *)p;
cout << share_buffer->name << " " << share_buffer->age << endl;
// unmap and close
munmap(p, read_size);
close(fd);
}
#endif
int main()
{
readMemory();
getchar();
return 0;
}
注意:
- 內存映射其實是將內存內容保存到了一個本地文件上,讀寫進程都操作這個文件映射對內存空間
- windows中必須兩個進程都開啓,寫進程可以不將文件落地,讀進程其實只是讀內存,linux的寫和讀進程可以分離,必須將文件落地,指向同一個文件路徑
- 可以傳輸複雜的結構體或者數組類型,需要計算好buffer的size,其中linux貌似杜宇string類型支持不好,改用char數組
- 共享內存在實際使用中需要根據讀寫切換進行加鎖保護
- linux還有另外一種共享內存的方式,shm相關函數,這種方式讀寫速度更快,但是文件不落地,共享內存大小受限