C++ 内存映射

这篇具有很好参考价值的文章主要介绍了C++ 内存映射。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

  在一个版图集成与分析工具的项目中看到了 一种 C++ 内存映射的用法,觉得非常强,分享一下大致的概念。

  随着制造工艺的不断进步,芯片版图文件越来越大,对于一些很大的文件,可能光是打开就需要几个小时,是芯片设计开发人员的一大痛点。

  于是我们领导想出了一个解决这个问题的办法:加载文件慢,是因为要在加载文件的过程中进行 parse, 把版图文件格式转换成自定义的数据格式,才能在工具中进行各种处理。采用内存映射的办法,只需要在第一次加载的时候做一次 parse,之后把自己管理的内存中的内容直接以 bit 的形式保存下来,下次加载的时候直接通过 reinterpret_cast 强制类型转换,把特定的 bit 位转换为对应的指针,就可以恢复 parse 之后的数据了,不需要再一次进行 parse,大大降低了打开文件的时间。

  使用分页方式管理程序的内存可以提高内存访问效率和节约存储空间。你可以将程序的内存划分为固定大小的页面,并按需保存或加载这些页面。通过指针类型转换,可以在加载时快速恢复页面的内存结构。

c++ 中创建对象

  在 C++ 中,一旦对象被创建,其大小通常是固定的,不会随着对象的使用而改变。对象的大小是在编译时确定的,它取决于类的成员变量和对齐规则。

  当你创建一个对象时,编译器会根据类的定义计算出对象所需的内存大小,并为对象分配足够的内存空间。这个大小在对象的生命周期中保持不变,除非你对对象进行了显式的重新分配或重新定义。

  需要注意的是,如果类中包含指针成员或动态分配的资源(如堆内存),对象的大小不会包括指针所指向的内存或动态分配的资源的大小。指针本身的大小是固定的,它保存了地址值而不是实际的数据。

  此外,编译器可能会对对象进行对齐以满足特定的对齐规则,这可能导致对象的大小增加。对齐规则可以保证访问对象的效率,并避免因对齐不当而引起的性能问题。

  总之,在一般情况下,一旦对象被创建,其大小是固定的,并且不会随着对象的使用而改变。对象的大小取决于类的成员变量和对齐规则,并且不包括指针所指向的内存或动态分配的资源的大小。

通过对象的首地址访问对象

  this 指针指向的地址是当前对象的内存地址。每个对象在内存中都有其自己的存储空间,包含了类的成员变量的值。this 指针指向这个存储空间的起始地址,使得成员函数可以通过 this 指针来访问对象的成员。

  只要知道对象存放的首地址,就可以强制转换成对象的指针,然后通过指针访问对象

例:

class A {
public:
    A(string s1) : _s1(s1) {}

    long long   show() { return reinterpret_cast<long long>(this); }
    void        s1() { cout << "s1: " << _s1 << endl; }

private:
    string      _s1;
};

int main() {
    A a("I'm a");

    long long addr = a.show();
    cout << "address: " << addr << endl;

    A* b = reinterpret_cast<A*>(addr); // 根据首地址获取对象指针
    b->s1();

    return 0;
}

/*********输出********************/
address: 247805771088
s1: I'm a

c++ 自己管理内存,创建对象

自己申请内存,在申请的内存上创建对象;通过对象的首地址获取对象

例:

class A {
public:
    A(string s1) : _s1(s1) {}

    long long       show() { return reinterpret_cast<long long>(this); }
    void            s1() { cout << "s1: " << _s1 << endl; }

private:
    string          _s1;
};

int main() {
    unsigned pagesize = 1 << 13; // 8k
    char* page = new char[pagesize]; // 申请了 8k 的内存;
    cout << "page: " << (void*)page << endl;

    A* a = new (page) A("I'm a"); // 在申请的内存上创建对象
    cout << "a: " << a << endl;
    A* b = reinterpret_cast<A*>(page); //直接通过地址获取对象
    b->s1();

    size_t shift = sizeof(A);
    page += shift; // 计算出对象 a 占用的内存

    A* c = new (page) A("I'm c"); // 在未被占用的内存上创建 c 对象
    A* d = reinterpret_cast<A*>(page); // 直接通过地址获取对象
    d->s1();

    return 0;
}
/********* 输出 **************/
page: 0x159ac205c50
a: 0x159ac205c50
s1: I'm a
s1: I'm c

把内存保存成文件再加载

上面讲过:对象的大小不会包括指针所指向的内存或动态分配的资源的大小,所以保存成文件的对象里不能有动态分配的内存(其实可以有,只是不能直接使用,要先初始化,申请内存,不然就会访问到不合法的内存),所以不能用 string,这里用 int 做成员变量举例。用有虚函数的类也比较麻烦,子类的大小难以计算。

例:

class A {
public:
    A(int num) : _num(num) {}

    void        show() { cout << "num: " << _num << endl; }

private:
    int             _num;
};

void saveMemoryToFile(const char* memory, size_t size, const string& filename) {
    ofstream file(filename, ios::binary);
    if (!file) {
        cerr << "Failed to open file for writing: " << filename << endl;
        return;
    }

    file.write(memory, size);
    file.close();
}

void readMemoryFromFile(char* memory, const string& filename) {
    ifstream file(filename, ios::binary);
    if (!file) {
        cerr << "Failed to open file for reading: " << filename << endl;
        return;
    }

    // 获取文件大小
    file.seekg(0, ios::end);
    size_t fileSize = file.tellg();
    file.seekg(0, ios::beg);

    // 读取文件内容
    file.read(memory, fileSize);
    file.close();
}

// 打印一段内存的内容,二进制表示,用于测试
void printMemoryBinary(const char* memory, std::size_t size) {
    for (std::size_t i = 0; i < size; ++i) {
        std::bitset<8> bits(memory[i]);
        std::cout << bits << ' ';
    }
    std::cout << std::endl;
}

int main() {
    unsigned pagesize = 1 << 13; // 8k
    char* page = new char[pagesize]; // 申请了 8k 的内存;

    size_t shift = sizeof(A);
    char* offset = page + shift; // 第二个 A 对象的起始地址
#if 1
    A* a = new (page) A(1160); // 在申请的内存上创建对象
    a->show();

    A* c = new (offset) A(5678); // 在未被占用的内存上创建 c 对象
    c->show();

    saveMemoryToFile(page, pagesize, "C:/lian/sandbox/testFile");
#else
    readMemoryFromFile(page, "C:/lian/sandbox/testFile");

    A* b = reinterpret_cast<A*>(page); //直接通过地址获取对象
    b->show();

    A* d = reinterpret_cast<A*>(offset); // 直接通过地址获取对象
    d->show();
#endif
    return 0;
}
  1. 把 #if 打开,执行程序。代码的作用是把类对象保存到文件里;

  2. 把 #if 关闭,打开 #else 中的代码,再执行程序。代码的作用是读取文件,直接把文件中的内容转换成类 A 的对象,

第一次输出:
num: 1160
num: 5678

第二次输出:
num: 1160
num: 5678

从结果可以看出来,成功通过访问内存的方式获取类对象。

总结

  对于比较大的数据文件,解析的时间较长,可以采用这种内存映射的方式保存出一份内存文件,这样就可以快速的打开文件,直接获取解析之后的数据格式。

博主个人网站文章来源地址https://www.toymoban.com/news/detail-536072.html

到了这里,关于C++ 内存映射的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM之jmap java内存映射工具

    jdk安装后会自带一些小工具, jmap 命令( Memory Map for Java )是其中之一。主要用于打印指定Java进程(或核 心文件、远程调试服务器)的共享对象内存映射或堆内存细节。 jmap命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的 大

    2024年02月04日
    浏览(24)
  • 【MMU】认识 MMU 及内存映射的流程

    MMU(Memory Manager Unit),是内存管理单元,负责将虚拟地址转换成物理地址。除此之外,MMU 实现了内存保护,进程无法直接访问物理内存,防止内存数据被随意篡改。 MMU: TLB模块:用于缓存从虚拟地址到物理地址的转换结果 TWU模块:负责完成页表的查过程 Cache:        

    2024年02月14日
    浏览(20)
  • 浅聊一下 C#程序的 内存映射文件 玩法

    前段时间训练营里有朋友问 内存映射文件 是怎么玩的?说实话这东西理论我相信很多朋友都知道,就是将文件映射到进程的虚拟地址,说起来很容易,那如何让大家眼见为实呢?可能会难倒很多人,所以这篇我以自己的认知尝试让大家眼见为实。 在任何讨论之前,内存文件

    2024年02月08日
    浏览(18)
  • 从内核世界透视 mmap 内存映射的本质(原理篇)

    本文基于内核 5.4 版本源码讨论 之前有不少读者给笔者留言,希望笔者写一篇文章介绍下 mmap 内存映射相关的知识体系,之所以迟迟没有动笔,是因为 mmap 这个系统调用看上去简单,实际上并不简单,可以说是非常复杂的一个系统调用。 如果想要给大家把 mmap 背后的技术本质

    2024年02月08日
    浏览(17)
  • [RK3399][Uboot]使用内存映射方式直接控制IO

    第一章 [RK3399][Uboot]使用内存映射方式直接控制IO 嵌入式工作这么多年,经常会用到GPIO,通常是使用内核的API或Sysfs来操作GPIO,直接操作GPIO寄存器的情况或GPIO内存映射的情况比较少,本文就针对RK3399平台的GPIO,以操作GPIO地址映射的方式来控制一下GPIO。会涉及到GPIO的IOMUX,

    2023年04月12日
    浏览(14)
  • 从内核世界透视 mmap 内存映射的本质(源码实现篇)

    本文基于内核 5.4 版本源码讨论 通过上篇文章 《从内核世界透视 mmap 内存映射的本质(原理篇)》的介绍,我们现在已经非常清楚了 mmap 背后的映射原理以及它的使用方法,其核心就是在进程虚拟内存空间中分配一段虚拟内存出来,然后将这段虚拟内存与磁盘文件映射起来,

    2024年02月08日
    浏览(36)
  • 第2章 Linux多进程开发 2.18 内存映射

    内存映射:可以进行进程间的通信 1.如果对mmap的返回值(ptr)做++操作(ptr++), munmap是否能够成功? void * ptr = mmap(…); ptr++; 可以对其进行++操作 munmap(ptr, len); // 错误,要保存地址 2.如果open时O_RDONLY, mmap时prot参数指定PROT_READ | PROT_WRITE会怎样? 错误,返回MAP_FAILED open()函数中的权限建议

    2024年02月10日
    浏览(16)
  • 【STM32】STM32内存映射以及启动过程(超详细过程)

    下图是 STM32F103xCDE 型号的内存映射图。 由于 STM32 是 32 位,且其地址总线也为 32 根,所以其 理论能够寻找的地址大小为 4GB 。 从上图可以看出,左边的地址从 0x0000 0000 ~ 0xFFFF FFFF 的 4GB 是 STM32 理论分配的地址空间, STM32 实际上的空间大小 远远小于 4GB 的 。4GB 中又划分出了

    2024年02月11日
    浏览(18)
  • 【操作系统基础】【CPU访存原理】:寄存 缓存 内存 外存、内存空间分区、虚拟地址转换、虚拟地址的映射

    存储器怎么存储数据、内存空间分区、虚拟地址转换 计算机的存储器:寄存 缓存 内存 外存(按功能划分) 计算机的处理器需要一个存储器来存储大量的指令和数据以便自己不断取指执行和访问数据。 内存 (内存就是运行内存,如手机的8G运行内存,电脑的16G运行内存)就

    2024年01月25日
    浏览(30)
  • 【IMX6ULL驱动开发学习】19.mmap内存映射

    mmap将一个文件或者其它对象映射进内存 ,使得应用层可以直接读取到驱动层的数据,无需通过copy_to_user函数 可以用于像LCD这样的外设, 需要读写大量数据的 一、应用层 mmap用法: 用open系统调用打开文件, 并返回描述符fd. 用mmap建立内存映射, 并返回映射首地址指针start. 对映

    2024年02月16日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包