程序(例如C或C ++)如何安排在计算机内存中?我对片段,变量等有一点了解,但基本上我对整个结构没有扎实的了解。
由于内存中的结构可能不同,我们假设Windows上有一个C ++控制台应用程序。
我特意指出的一些指示:
欢迎链接到类似教程的材料,但请不要参考汇编程序等知识的参考样式材料。
答案 0 :(得分:10)
这可能是你想要的:
http://en.wikipedia.org/wiki/Portable_Executable
PE文件格式是Windows二进制文件(.exe,.dll等)的二进制文件结构。基本上,它们被映射到内存中。这里描述了更多细节,并解释了你自己如何看待内存中加载的dll的二进制表示:
http://msdn.microsoft.com/en-us/magazine/cc301805.aspx
编辑:
现在我知道您想了解源代码如何与PE文件中的二进制代码相关。这是一个巨大的领域。
首先,您必须了解有关计算机体系结构的基础知识,这将涉及学习汇编代码的一般基础知识。任何“计算机体系结构简介”大学课程都可以。文献包括例如“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。
然后我们调用一个意味着两件事的函数。
计算机必须将二进制PE文件的一部分加载到包含函数SomeFunction()的内存中。无论你创建多少个SimpleClass实例,SomeFunction都只会在内存中一次。
计算机必须执行SomeFunction()函数。这意味着几件事:
我建议,你自己建立一个简单的例子并逐步完成反汇编。在调试版本中,代码将易于理解,Visual Studio在反汇编视图中显示变量名称。查看寄存器esp,ebp和eip的作用,分配对象的内存位置,代码所在的位置等。
答案 1 :(得分:4)
这是一个多么大的问题!
首先,您想了解virtual memory。没有它,没有别的意义。简而言之,C / C ++指针不是物理内存地址。指针是虚拟地址。有一个特殊的CPU功能(MMU,内存管理单元)透明地将它们映射到物理内存。仅允许操作系统配置MMU。
这提供了安全性(没有C / C ++指针值可以指向另一个进程的虚拟地址空间,除非该进程是故意与你共享内存)并让操作系统做一些我们现在非常神奇的事情理所当然(比如透明地将一些进程的内存交换到磁盘,然后在进程尝试使用它时透明地加载它)。
进程的地址空间(即k.a.虚拟地址空间,a.k.a。可寻址内存)包含:
为Windows内核保留的巨大内存区域,不允许该进程触及;
“未映射”的虚拟内存区域,即没有加载任何内容,没有物理内存分配给这些地址,如果尝试访问它们,进程将崩溃;
将已加载的各种模块(EXE和DLL文件)分开(每个模块包含机器代码,字符串常量和其他数据);以及
进程从系统分配的其他内存。
现在通常一个进程允许C运行时库或Win32库执行大多数超级低级内存管理,包括设置:
一个堆栈(对于每个线程),其中存储了局部变量和函数参数以及返回值;以及
一个堆,如果进程调用malloc
或new X
,则分配内存。
有关堆栈结构的更多信息,请阅读calling conventions。有关堆的结构的更多信息,请阅读malloc implementations。通常,堆栈实际上是一个堆栈,一个后进先出的数据结构,包含参数,局部变量和偶尔的临时结果,而不是更多。由于程序很容易直接写入堆栈末尾(此站点命名后的常见C / C ++错误),因此系统库通常会确保堆栈旁边有一个未映射的页面。这使得进程在发生此类错误时立即崩溃,因此调试起来要容易得多(并且该进程在可以造成更多损害之前就会被终止)。
在数据结构意义上,堆实际上不是堆。它是由CRT或Win32库维护的数据结构,它从操作系统获取内存页,并在进程通过malloc
和朋友请求小块内存时将它们包装出来。 (请注意,操作系统不会对此进行微观管理;如果它不喜欢CRT的方式,则可以在很大程度上管理其所需的地址空间。)
流程还可以使用VirtualAlloc
或MapViewOfFile
等API直接从操作系统请求页面。
还有更多,但我最好停下来!
答案 2 :(得分:1)
要了解堆栈帧结构,您可以参考 http://en.wikipedia.org/wiki/Call_stack
它为您提供有关调用堆栈结构,本地,全局,返回地址如何存储在调用堆栈中的信息
答案 3 :(得分:1)
答案 4 :(得分:0)
这可能不是最准确的信息,但MS Press提供了本书Inside Microsoft® Windows® 2000, Third Edition的一些示例章节,其中包含有关过程及其创建的信息以及一些重要数据结构的图像。
我偶然发现this PDF在一张漂亮的图表中总结了一些上述信息。
但所有提供的信息更多来自操作系统的观点,而不是关于应用程序方面的详细信息。
答案 5 :(得分:0)
实际上 - 在这个问题上,你至少可以在Assembler中获得一点点知识。我会重新建立一个倒车(教程)网站,例如OpenRCE.org。
答案 6 :(得分:0)
史蒂文斯的书“高级Unix编程”有几个页面,如果你能掌握它,就会得到确切答案。当然,你应该拥有这本书。