我最近对硬件非常感兴趣,一直在研究 CPU 的工作原理。为此,我一直在为虚构的 CPU 架构制作汇编器、反汇编器和 vCPU。
我学到了很多,但还是有些东西不明白。汇编程序/CPU 如何处理变量?
据我所知,局部变量是在堆栈上分配和存储的。但是当 CPU 到达需要局部变量值的指令时,它如何知道它在堆栈上的位置?汇编程序不能在编译时输入地址,对吗?
我不太理解全局变量,除非你在.data
部分 (x86 asm) 中有它们。当 CPU 到达需要全球的变量,它怎么知道它在内存中的位置?每次运行程序时,它不是在不同的位置吗?那么汇编程序就不能给它一个地址,对吗?
我一直在各处寻找答案,但还没有找到。
答案1
在 CPU 层面上,它们不是变量,它们只是地址、寄存器和其他位置。堆栈不一定是它们存储的地方。
虽然你可能有更高级别的代码
Add `variable1` to `variable2`
在机器代码级别,这将被分解为更简单的指令(使用伪代码,而不是真正的 x86 汇编)
Read memory address of `variable1` into register A
Read memory address of `variable2` into register B
Add resister A to register B
Store result in memory address of `variable3`
变量名仅供人类使用,CPU 并不关心它们,并且它们将被汇编程序替换为相关的程序内存地址。
据我所知(已经 20 年了),堆栈不是用于随机访问的。你可以将值推送到堆栈上,也可以弹出最后一个项目,但这give me item 9 on the stack
不是它的工作原理。充其量
Pop item from stack into register A
Pop (next) item from stack into register B
Add register A to register B
...
如果你需要堆栈上的第 9 个项目,那么你必须先弹出前 8 个项目,然后大概先对它们进行必要的操作。它是一个队列,而不是随机存储。具体来说,它是一个LIFO(后进先出)队列。
举个例子,如果你要调用一个函数,你很可能将两个项目推送到堆栈上以作为“变量”传递。接收代码要做的第一件事就是将它们从堆栈中弹出:
Main code:
Push variable2 onto stack
Push variable1 onto stack
Jump to code at address (AddNumbers)
AddNumbers:
Pop variable 1 from stack to register A
Pop variable 2 from stack to register B
Add register A to register B
(请注意将项目压入堆栈的相反顺序)
如何将变量保留为局部变量和全局变量并不是 CPU 真正关心的事情。这是一个语义和描述性语言的问题,汇编程序使用它们来决定在内存中将命名变量安排在何处,然后再用机器代码中的地址替换它们的名称,以及赋予在局部范围内重复使用简单名称的能力。
汇编代码可能“接近裸机”,但仍存在混淆,例如变量名、本地和全局范围,以及其他用于使它们更易于人类处理的东西。它们仍然需要一个程序(汇编程序)将它们重写为实际的机器代码,然后 CPU 才能使用它们。