C++ 中的非继承类有两种访问权限, privatepublic. 一般来说, private 只允许类型内部使用, 不允许外部访问. 然而, 由于 C++ 语言巨大的复杂性, 这一约束存在若干漏洞. 一种简单的做法就是假设 C++ 编译器不会对类对象的内存布局做奇怪的扰动, 这一假设通常是正确的, 因此可以通过猜测私有成员相对对象整体的地址偏移量访问私有成员. 或者更加方便地通过定义一个成员完全相同但访问权限相反的类, 并将原类对象的指针转换成新类的指针的方式访问原类对象的私有指针.

这两种方法本质上都基于「假设」, 即假设 C++ 编译器不会对类型的成员做特殊的处理. 本文介绍一种更加优雅的方法, 可以稳定地实现对私有成员的访问.

阅读全文 »

C++ 中以继承的方式实现动态多态是存在额外开销的, 具体地说,C++ 对象使用了额外的内存保存关于基类信息才实现了运行时多态. 但这种额外信息究竟以何种方式在对象的内存中报错对我来说仍是未知. 受到前人文章的启发, 本文借助 GDB 调试器一步步地观察记录 C++ 对象的内存布局, 力求理解实际 C++ 编译器 (gcc 9.4.0) 对 C++ 对象的内存布局以及派生类的实现.

阅读全文 »

Memory Barrier 是 CPU 提供的一种机制, 它的作用是在程序中设置一个内存屏障, CPU 会保证所有在 Memory Barrier 之前的代码对内存的访问 (改动) 在 Memory Barrier 之后都是可见的. 这一保证据有两方面的效应:

  1. 所有 Memory Barrier 之后的代码都不能被重排 (Reordering) 到 Memory Barrier 之前执行
  2. 当执行到 Memory Barrier 时, CPU 必须在所有 Core 之间同步 Cache, 保证此刻所有 Core 看到的内存是一致的.

Memory Order 是 C++ 11 提供的一种并发编程机制, 它允许显式地限制编译器/CPU 对指令的重排 (Reordering), 以及显式地控制多核 CPU Cache 之间的同步. 其效果是使得不同线程/不同 CPU Core 所观察到的原子操作附近的内存操作具有可预测的顺序.

阅读全文 »

对于被广泛使用的标准库或其他库, 如果一个系统中多个程序都在引用相同的符号, 此时使用静态库将需要为每个程序都加载一份符号定义的代码和数据, 这对内存是极大的浪费.

共享库 (shared library) 是解决静态库这一问题的产物. 共享库是一个目标模块, 在运行或加载时可以被加载到任意内存地址, 并和一个在内存中的程序链接起来. 这一过程称为动态链接, 是由一个叫做 动态链接器 (dynamic linker) 的程序执行的.

阅读全文 »

链接器 链接 的结果是生成一个可执行目标文件, 这个目标文件中包含将程序加载到内存并运行的所有信息. 下图是一个 ELF 可执行目标文件所包含的信息:

阅读全文 »

现代 x86-64 系统使用可执行可链接格式文件 (Executable and Linkable Format, ELF)

ELF 文件的结构:

阅读全文 »

程序从源代码到可执行文件需要经过 4 个步骤, 链接就是其中重要的一步.

链接器的主要工作是将具备引用关系的不同编译单元的编译结果 (i.e. 可重定位目标文件) 相互连接, 组成一个可执行文件.

静态链接器以一组可重定位目标文件和命令行参数作为输入, 生成一个完全链接的, 可以加载运行的可执行目标文件.

阅读全文 »

计算机存储器具有金字塔式结构, 即: 较高层的存储器容量小速度快, 低层的存储器容量大速度慢, 计算机通过缓存的方式提高储存系统的整体性能.

阅读全文 »
0%