【发布时间】:2019-12-28 08:22:34
【问题描述】:
警告:这很长,但我希望它对将来像我这样的人有用。
我想我知道程序计数器是什么,惰性内存分配是如何工作的,MMU 是做什么的,虚拟内存地址如何映射到物理地址以及 L1、L2 缓存的用途。我真正遇到的问题是,当我们运行 C 代码时,它们如何在高层次上结合在一起。
假设我有这个 C 代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* ptr;
int n = 1000000, i = 0;
// Dynamically allocate memory using malloc()
ptr = (int*)malloc(n * sizeof(int));
ptr[0] = 99;
i += 100;
printf("%d\n", ptr[0]);
free(ptr);
return 0;
}
所以这是我将所有内容放在一起的尝试:
在调用
execve()之后,部分可执行文件被加载到内存中,例如文本和数据段,但大部分代码不是——它们是按需加载的(按需分页)。第一条指令的地址在进程表的程序计数器 (PC) 字段中以及物理上的 PC 寄存器中,可供使用。
随着 CPU 执行指令,PC 被更新(通常 +1,但跳转可以转到不同的地址)。
进入主函数:
ptr、n、i在栈中。接下来,当我们调用
malloc时,C 库会要求操作系统(我认为是通过sbrk()sys 调用,还是mmap()?)在堆上分配一些内存。malloc在这种情况下成功,返回一个虚拟内存地址 (VMA),但物理内存可能尚未分配。页表不包含VMA,所以当CPU试图访问这样的VMA时,会产生缺页。在我们的例子中,当我们执行
ptr[0] = 99时,CPU 会引发页面错误。我不确定是分配了整个数组还是仅分配了第一页(4k 大小)。
但现在我不知道如何将缓存访问放入图片中。 i如何放入L1缓存?它与 VMA 有什么关系?
对不起,如果这令人困惑。我只是希望有人可以帮助我完成整个过程...
【问题讨论】:
-
是的,您的这些步骤是正确的。操作系统不知道您的数组大小,因此页面错误只会带来该页面(尽管它可以通过预取进行优化)。缓存只是复制主内存内容以加快访问速度。它没有用户可见的地址。
-
感谢您查看@Jester。您能否澄清“不知道数组大小,所以引入该页面”?具体是哪一页,抱歉。另外,我想我在试图弄清楚数据访问的顺序时迷失了方向。我有一个虚拟机地址,我想加载
i的值。 CPU怎么知道i"的值存储在这个地址的缓存中?L1缓存中也有TLB吗? -
缓存是一种优化。它们的管理可能会变得复杂,但以更简单的形式,L1 和 L2 只是“所有”内存访问“必须”通过的两个仓库。如果 CPU 在这样的仓库中找到它需要的东西,它就不会进入内存。缓存独立于分页,它们影响到内存的“一切”。当然,如果没有架构标签,就不可能完全正确(实际上我的陈述过于简单化了)。 CPU 设计人员在优化方面非常有创意,但他们不能总是负担得起缓存(端口压力、传输器......)
-
在 linux 上,您可以使用
strace来查看程序执行期间进行了哪些系统调用。 -
OT:关于:
ptr = (int*)malloc(n * sizeof(int));1)malloc()返回类型:void*,可以分配给任何指针。强制转换只会使代码混乱,使其更难以理解、调试等。 2) 参数应该具有类型:size_t然而,变量n具有类型int。编译器应该告诉您这种隐式转换的风险。 3) 始终检查 (!=NULL) 返回值以确保操作成功。如果不成功,请调用perror()将您的错误消息和失败的文本原因输出到stderr
标签: c assembly memory operating-system malloc