如何访问内存地址的内容?

val*_*237 3 c++

问题:

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)

这只是一个实验。我不知道是否可能。

Hen*_*her 7

您可以使用称为共享内存的概念来做到这一点。

使用共享内存,您可以共享(作为示例)如下所示的结构:

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