CPU和内存的通信

3
我是一个初学者程序员,但我希望更深入地了解一些东西。我做了一些研究并阅读了很多文本,但仍然无法理解某些事情。
编写基本的东西(使用C语言)时:
int myNumber;
myNumber = 3;
printf("Here's my number: %d", myNumber);

我发现(主要是在32位CPU上),整数占用32位即4个字节。因此,在我的代码的第一行,CPU进入内存。内存是按字节寻址的,因此CPU选择4个连续的字节来存储我的变量,并将地址存储到第一个(或最后一个)字节中。
在我的代码的第二行,CPU使用存储的MyNumber变量地址,进入内存并找到32位保留空间。它的任务现在是在那里存储数字“3”,因此它用序列00000000-00000000-00000000-00000011填充这四个字节。在第三行,它做同样的事情- CPU进入内存中的那个地址,并加载存储在该地址中的数字。
(第一个问题-我是否理解得正确?)
我不明白的是:
32位CPU的地址大小(指向该变量的指针)为4个字节。(这就是为什么32位CPU最多只能使用4GB的内存-因为只有2^32个不同的地址,二进制长度为32)
现在,CPU把这些地址存储在哪里?它是否具有自己的内存或高速缓存来存储它们?为什么它将32位长的地址存储到32位长的整数中呢?如果大小相同,将实际数字存储在其缓存中是否更好?
最后一个问题-如果它在自己的高速缓存中存储所有这些整数的地址,并且长度相同(4个字节),则需要与实际变量相同的空间来存储地址。但是变量可以占用多达4GB的空间,因此CPU必须具有4GB的自己的空间来存储这些变量的地址。听起来很奇怪。
谢谢帮助!
我正在努力理解这个问题,但它太困难了。

这些是非常有趣的问题。我也想看看答案是什么。 - Ashley Davies
4个回答

5

(第一个问题 - 我理解得对吗?)

首先要认识到的是,该值可能根本没有存储在主内存中。编译器可能会决定将其存储在寄存器中,因为这更优化。1

内存是字节可寻址的,因此CPU选择4个连续字节来存储我的变量,并将地址存储到第一个(或最后一个)字节。

假设编译器确实决定将其存储在主内存中,则在32位机器上,int通常为4字节,因此将分配4字节进行存储。

在32位CPU中,该地址(指向该变量的指针)的大小为4字节。(这就是为什么32位CPU只能使用最大4GB的内存,因为二进制长度为32的不同地址只有2^32个。)

请注意,int的宽度和指针的宽度不必相同,因此地址空间的大小不一定与它们之间存在联系。

那么,CPU将这些地址存储在哪里呢?

对于局部变量,地址实际上被硬编码到可执行文件本身中,通常作为堆栈指针的偏移量。

对于动态分配的对象(即使用malloc分配的东西),程序员通常维护一个相应的指针变量(否则就会出现内存泄漏!)。该指针也可能是动态分配的(在复杂数据结构的情况下),但如果你回溯到足够远的地方,最终会达到某个局部变量。在这种情况下,上述规则适用。

但是变量可以占用4GB的空间,因此CPU必须拥有自己的4GB空间来存储这些变量的地址。

如果您的程序由独立地malloc数百万个int组成,则是的,您将需要与之相同数量的存储空间来存储指针。但大多数程序看起来并非如此。您通常会分配更大的对象(例如数组或大型结构体)。

缓存

代码存储的具体位置是与架构相关的。在现代x86上,通常有2或3层高速缓存位于CPU和主内存之间。但是缓存不是可以独立寻址的;CPU无法决定将int存储在高速缓存中,而不是主内存中。相反,缓存实际上是主内存的子集的冗余副本。

另一个需要考虑的问题是,编译器通常在分配对象存储时处理虚拟地址。在现代x86上,这些地址被专用硬件映射到物理地址 (即对应于主内存中实际存储字节的地址),同时配合操作系统支持。


1. 或者编译器完全可以将其优化掉。


谢谢!只是为了确认 - 当我在我的代码中添加 printf("%p", &myVariable); 时,我看到的结果例如 0022FF4A - 只是十六进制的32位数字。所以那不是实际的物理地址,而只是一些虚拟地址?(我想尝试一些内存管理实验,因为C语言似乎非常符合这个.. :)) - Jeyekomon
@Jeyekomon:一旦您开始观察对象的地址,编译器就别无选择,只能为其分配一个地址。但如果您不观察它,则编译器可以自由地仅将其存储在寄存器中。 - Oliver Charlesworth
@Jeyekomon:是的,假设你正在使用具有虚拟地址系统的平台,那么这就是一个虚拟地址。 - Oliver Charlesworth

3
在我的代码的第二行中,CPU使用存储的MyNumber变量地址,进入内存并找到那里32位保留空间。几乎正确。内存基本上是无结构的。CPU看不到有32位“保留空间”。但是CPU被指示读取32位数据,因此它从指定地址开始读取32位数据。然后,只需希望/假设这32位实际上包含某些有意义的内容。
CPU有少量寄存器可用于存储数据(常见CPU具有8、16或32个寄存器,因此它们只能保存您正在处理的特定变量)。因此,首先回答最后一部分,编译器当然可能会(并且可能会)生成代码,只需将您的int存储到寄存器中,而不是将其存储在内存中,并告诉CPU从指定地址加载它。至于问题的另一部分:最终,程序的每个部分都存储在内存中。其中一部分是指令流,另一部分是散布在内存中的数据块。
有一些技巧可以帮助定位CPU所需的数据:程序的一部分内存包含堆栈,堆栈通常在范围内存储局部变量。 CPU始终在其寄存器中维护指向堆栈顶部的指针,因此它可以通过修改堆栈指针来轻松地定位堆栈上的数据,只需使用固定偏移量。指令可以直接包含这些偏移量,因此为了读取您的int,编译器例如可以在进入函数时生成将int写入堆栈顶部的代码,然后当需要引用该函数时,具有读取所找到的数据的代码地址,堆栈指针指向它,再加上需要定位变量的小偏移量。

0
请注意,您的程序看到的地址可能不是(或很少是)从“内存开始”或0开始的物理地址。它们大多是偏移量,指向特定内存块,其中内存管理器知道真实地址,并通过基址+偏移量访问作为真实数据存储。
我们确实需要内存,因为缓存是有限的 ;-)
马里奥

0

在CPU内部,有一个寄存器包含下一条要执行的指令的地址。指令本身会记录变量的位置信息。如果该变量被优化,那么指令可能会指向一个寄存器,但通常情况下,指令会指向被访问的变量的地址。你的代码在编译和加载到内存后,所有这些都嵌入其中!我建议你研究汇编语言,以更好地理解这些内容。祝你好运!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接