在file1.cpp中,我将一些数据保存到内存地址为 0x0364DF00 的内存中。
在file2.cpp中,如何访问内存地址(0x0364DF00)的内容?
我尝试添加一些代码来解决这个问题,但没有成功。
#include <iostream>
using namespace std;
int main() {
int* address = (int*)0x0364DF00;
cout << address(&address);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这只是一个实验。我不知道是否可能。
您可以使用称为共享内存的概念来做到这一点。
使用共享内存,您可以共享(作为示例)如下所示的结构:
struct Data {
std::atomic<int> counter;
};
Run Code Online (Sandbox Code Playgroud)
请注意,由于您将在不同的核心之间共享数据,因此需要正确同步。在本例中我们使用 std::atomic。
然后,您需要一种机制将此内存映射到共享位置,这是特定于操作系统的,但通常包括提供一个文件作为内存的命名位置和大小(以调整文件大小)。一旦打开,文件就可以被内存映射,并且指向内存位置的指针将返回给用户。
一般来说,制作者(或创建者)被赋予文件名和大小,如下所示:
// And this in file1.cpp
void child() {
MappedFile file;
if ( !file.open( "/tmp/file", 1024 ) ) return;
Data* data = reinterpret_cast<Data*>(file.data());
for ( unsigned j=0; j<=10; ++j ) {
data->counter = j;
::usleep( 1000000 );
}
}
Run Code Online (Sandbox Code Playgroud)
然后另一个进程可以查看该文件,获取其大小并对其进行完整的内存映射,如以下代码所示。映射位置返回的指针指向与前一个指针相同的物理内存。但是,由于这两个函数运行在不同的进程上,因此它们的虚拟映射会不同,因此它们的数值也很可能会不同。
// This can be in file2.cpp
void parent() {
MappedFile file;
while ( !file.open( "/tmp/file" ) ) {}
Data* data = reinterpret_cast<Data*>( file.data() );
int last = 0;
while ( data->counter < 10 ) {
if ( last!=data->counter ) {
std::cout << "Counter:" << data->counter << std::endl;
last = data->counter;
}
}
}
Run Code Online (Sandbox Code Playgroud)
内部实现是特定于操作系统的,但在 Linux 上,它看起来如下所示。这是我为测试而编写的示例,因此它不是生产质量,甚至不是经过充分测试的(但适用于本示例)。
首先定义一个带有整数的类来保存文件描述符、文件大小和共享内存位置。
struct MappedFile {
MappedFile() {}
~MappedFile() { close(); }
private:
int _fid = -1;
void* _addr = nullptr;
std::size_t _mapsize = 0;
};
Run Code Online (Sandbox Code Playgroud)
然后创建一种方法来打开/创建给定大小的文件。这就是“创造者”。它使用标志打开文件O_CREAT,如果文件不存在,则创建它。然后,它将文件截断为提供的大小,该大小首先四舍五入为页面大小(在 Linux 上通常为 4k)——这是一个mmap()要求。然后文件被内存映射并且指针返回给用户。
bool open( const std::string& filename, std::size_t filesize ) {
int fid = ::open( filename.c_str(),
O_CREAT|O_RDWR, S_IRWXU | S_IRWXG);
if ( fid<0 ) {
std::cerr << strerror(errno) << std::endl;
std::cerr << "Could not create file " << filename << std::endl;
return false;
}
long mapsize = ((filesize-1)/PAGESIZE + 1 )*PAGESIZE;
int res = ftruncate( fid, mapsize );
if ( res!=0 ) {
std::cerr << strerror(errno) << std::endl;
std::cerr << "Could not trucate file" << std::endl;
::close(fid);
return false;
}
void* addr = mmap( nullptr, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fid, 0 );
if ( addr==MAP_FAILED ) {
std::cerr << "Could not memory map region" << std::endl;
::close(fid);
return false;
}
close();
_fid = fid;
_addr = addr;
_mapsize = mapsize;
return true;
}
Run Code Online (Sandbox Code Playgroud)
还有另一种方法依赖于文件已经创建的事实。请注意,可能存在竞争条件,即消费者在创建文件之后但在将其截断为正确大小之前打开文件。因此,我们检查文件大小是否为零。其余部分相同,只是我们不调整文件大小。
bool open( const std::string& filename ) {
struct stat sb;
if ( stat( filename.c_str(), &sb ) != 0 ) {
return false;
}
size_t filesize = sb.st_size;
if ( filesize == 0 ) return false;
int fid = ::open( filename.c_str(), O_RDWR, S_IRWXU | S_IRWXG);
if ( fid<0 ) {
std::cerr << strerror(errno) << std::endl;
std::cerr << "Could not open file " << filename << std::endl;
return false;
}
void* addr = mmap( nullptr, filesize, PROT_READ|PROT_WRITE, MAP_SHARED, fid, 0 );
if ( addr==MAP_FAILED ) {
std::cerr << "Could not memory map region" << std::endl;
::close(fid);
return false;
}
close();
_fid = fid;
_addr = addr;
_mapsize = filesize;
return true;
}
Run Code Online (Sandbox Code Playgroud)
请注意,我说的是“生产者”和“消费者”,但这两个进程都具有对映射内存的读写访问权限。
销毁对象对于两个进程来说都是相同的,就像取消内存映射和关闭文件一样简单。
void close() {
if ( _addr!=nullptr ) {
::munmap( _addr, _mapsize );
::close(_fid);
}
_addr = nullptr;
_fid = -1;
_mapsize = 0;
}
Run Code Online (Sandbox Code Playgroud)
此测试的示例驱动程序会生成一个单独的进程并fork()继续共享信息
int main() {
pid_t pid = fork();
if ( pid == 0 ) child(); else parent();
}
Run Code Online (Sandbox Code Playgroud)
这会产生
Counter:1
Counter:2
Counter:3
Counter:4
Counter:5
Counter:6
Counter:7
Counter:8
Counter:9
Counter:10
Run Code Online (Sandbox Code Playgroud)
Godbolt 上的完整代码:https ://godbolt.org/z/T8a5enzje
| 归档时间: |
|
| 查看次数: |
238 次 |
| 最近记录: |