memory - 程序在内存中的外观如何?

标签 memory winapi program-structure

程序(例如 C 或 C++)在计算机内存中是如何排列的?我对段、变量等有点了解,但基本上我对整个结构没有扎实的了解。

由于内存中的结构可能不同,我们假设 Windows 上有一个 C++ 控制台应用程序。

一些指向我所追求的具体目标:

  • 函数概要,如何调用?
  • 每个函数都有一个堆栈帧,它包含什么以及它在内存中是如何排列的?
  • 函数参数和返回值
  • 全局变量和局部变量?
  • const 静态变量?
  • 线程本地存储..

欢迎链接到类似教程的 Material 等,但如果您了解汇编程序等知识,请不要引用风格的 Material 。

最佳答案

这可能是您正在寻找的:

http://en.wikipedia.org/wiki/Portable_Executable

PE 文件格式是 windows 二进制文件(.exe、.dll 等)的二进制文件结构。基本上,它们像这样映射到内存中。此处描述了更多详细信息,并解释了您自己如何查看内存中加载的 dll 的二进制表示:

http://msdn.microsoft.com/en-us/magazine/cc301805.aspx

编辑:

现在我了解到您想了解源代码与 PE 文件中的二进制代码之间的关系。这是一个巨大的领域。

首先,您必须了解有关计算机体系结构的基础知识,这将涉及学习汇编代码的一般基础知识。任何“计算机体系结构概论”大学类(class)都可以。文学包括例如“John L. Hennessy 和 David A. Patterson。计算机体系结构:定量方法”或“Andrew Tanenbaum,结构化计算机组织”。

读完之后,你应该明白什么是栈,以及它与堆的区别。堆栈指针和基指针是什么,返回地址是什么,有多少个寄存器等等。

一旦你理解了这一点,就比较容易把各个部分放在一起:

C++ 对象包含代码和数据,即成员变量。一类

class SimpleClass {
     int m_nInteger;
     double m_fDouble;

     double SomeFunction() { return m_nInteger + m_fDouble; }
}

将是内存中的 4 + 8 个连续字节。当你这样做时会发生什么:

SimpleClass c1;
c1.m_nInteger = 1;
c1.m_fDouble = 5.0;
c1.SomeFunction();

首先,在栈上创建对象 c1,即栈指针 esp 减少 12 个字节以腾出空间。然后常量“1”被写入内存地址esp-12,常量“5.0”被写入esp-8。

然后我们调用一个函数,这意味着两件事。

  1. 计算机必须将二进制 PE 文件的部分加载到包含函数 SomeFunction() 的内存中。无论您创建多少个 SimpleClass 实例,SomeFunction 只会在内存中存在一次。

  2. 计算机必须执行函数 SomeFunction()。这意味着几件事:

    1. 调用函数也意味着传递所有参数,这通常是在堆栈上完成的。 SomeFunction 有一个(!)参数,this 指针,即指向我们刚刚写入值“1”和“5.0”的堆栈上的内存地址的指针
    2. 保存当前程序状态,即当前指令地址,即 SomeFunction 返回时将执行的代码地址。调用函数意味着将返回地址压入堆栈,并将指令指针(寄存器 eip)设置为函数 SomeFunction 的地址。
    3. 在函数 SomeFunction 中,通过将旧的基指针 (ebp) 存储在堆栈上 (push ebp) 并将堆栈指针作为新的基指针 (mov ebp, esp) 来保存旧堆栈。
    4. 执行 SomeFunction 的实际二进制代码,该代码将调用将 m_nInteger 转换为 double 并将其添加到 m_fDouble 的机器指令。 m_nInteger 和 m_fDouble 在堆栈中找到,位于 ebp - x 字节处。
    5. 加法的结果存储在寄存器中,函数返回。这意味着堆栈被丢弃,这意味着堆栈指针被设置回基指针。基指针被设置回(堆栈上的下一个值),然后指令指针被设置到返回地址(又是堆栈上的下一个值)。现在我们回到了原始状态,但在某些寄存器中隐藏着 SomeFunction() 的结果。

我建议,您自己构建一个这样简单的示例并逐步进行反汇编。在调试构建中,代码将很容易理解,Visual Studio 在反汇编 View 中显示变量名称。查看寄存器 esp、ebp 和 eip 的作用,对象在内存中的分配位置,代码在哪里等。

关于memory - 程序在内存中的外观如何?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1769590/

相关文章:

performance - Scala 编译器如何处理未使用的变量值?

delphi - 常规过程/方法指针

programming-languages - 程序入口点名称 'main' 的由来?

python - For-Else 循环在另一个 For 循环中

使用编译指示包和继承的 C++ 对齐

c - 空指针取消引用未初始化的内存是否属于内存损坏

C - 返回指针的函数,如何编写高效的代码?

c# - 如何在 C# 中读取 Win32 WM_MOVE lParam x,y 坐标?

c++ - RegSetValueEx 线程安全吗?