进程如何跟踪其局部变量
How does a process keep track of its local variables
据我所知,当一个进程分配局部变量时,它通过将它们作为堆栈推入内存来实现,但仍然通过使用堆栈指针的偏移量来引用它们来将它们作为随机内存访问(来自此线程 What is the idea behind using a stack for local variables?).
但是,它怎么知道哪些变量有什么偏移量呢?我的思考方式是否正确?
局部变量的偏移量是"baked into"作为常量的机器代码。当编译器完成时,您的程序称为局部变量的东西将替换为编译器分配的固定内存偏移量。
假设您声明了三个局部变量:
char a[8];
int b;
short c;
编译器为这些变量分配偏移量:a
位于偏移量 0
,b
位于偏移量 8
,c
位于偏移量12
。假设您的代码确实 b += c
。编译器将其翻译成如下所示的代码块:
LOAD @(SP+8)
ADD @(SP+12)
STORE @(SP+8)
这里唯一改变的值是 SP
(堆栈指针)。所有偏移量都是数字常量。
前言:以下文字以x86架构为例。其他架构确实以不同的方式处理事情。
[...] it does so by pushing them into memory as a stack, [...]
差不多了。 它通过将它们推入 [当前进程的] 堆栈 的内存中来实现。每个 进程 都有自己的堆栈。因此,随着每个上下文切换,这个 堆栈框架 确实会发生变化 - 它的 局部变量(在堆栈上).
也会发生变化
通常(!)本地定义的变量是相对于 堆栈框架 保存并存在于 EBP
寄存器中的引用。这与 全局定义的 变量形成对比,后者是相对于 数据段基础 引用的。所以每个进程都有自己的堆栈和自己的局部变量。
较新的编译器可以节省寄存器 EBP
并引用与 ESP
寄存器相关的变量。这有两个后果:
- 多一个可用寄存器
- 少了一种调试的可能性(调试时经常使用
EBP
值作为当前Stack Frame的参考来识别局部变量)。因此,如果没有单独的 调试信息文件 . ,这会使调试变得更加困难
所以回答你的主要问题
How does a process keep track of its local variables
进程跟踪它们的栈帧(其中包含局部变量),但不跟踪它们的局部变量变量 本身。 堆栈框架随着每个进程切换而变化。 局部变量只是相对于保存在寄存器EBP
中的堆栈帧指针(或相对于Stack Pointer ESP
, 取决于编译器设置).
编译器完成记忆偏移量的工作。这些偏移量只是硬编码的。就像将变量加载到寄存器(例如加载到 eax)一样,编译器会产生类似 mov eax, [esp-4]
的结果,其中 esp 是堆栈指针寄存器,4 是偏移量。如果新变量将被推送到下一个 mov 到 get/set 变量将有更大的偏移量。这一切都是编译时间分析。
此外,某些平台上的堆栈可能会颠倒 - 因此偏移量将为正。
据我所知,当一个进程分配局部变量时,它通过将它们作为堆栈推入内存来实现,但仍然通过使用堆栈指针的偏移量来引用它们来将它们作为随机内存访问(来自此线程 What is the idea behind using a stack for local variables?).
但是,它怎么知道哪些变量有什么偏移量呢?我的思考方式是否正确?
局部变量的偏移量是"baked into"作为常量的机器代码。当编译器完成时,您的程序称为局部变量的东西将替换为编译器分配的固定内存偏移量。
假设您声明了三个局部变量:
char a[8];
int b;
short c;
编译器为这些变量分配偏移量:a
位于偏移量 0
,b
位于偏移量 8
,c
位于偏移量12
。假设您的代码确实 b += c
。编译器将其翻译成如下所示的代码块:
LOAD @(SP+8)
ADD @(SP+12)
STORE @(SP+8)
这里唯一改变的值是 SP
(堆栈指针)。所有偏移量都是数字常量。
前言:以下文字以x86架构为例。其他架构确实以不同的方式处理事情。
[...] it does so by pushing them into memory as a stack, [...]
差不多了。 它通过将它们推入 [当前进程的] 堆栈 的内存中来实现。每个 进程 都有自己的堆栈。因此,随着每个上下文切换,这个 堆栈框架 确实会发生变化 - 它的 局部变量(在堆栈上).
也会发生变化 通常(!)本地定义的变量是相对于 堆栈框架 保存并存在于 EBP
寄存器中的引用。这与 全局定义的 变量形成对比,后者是相对于 数据段基础 引用的。所以每个进程都有自己的堆栈和自己的局部变量。
较新的编译器可以节省寄存器 EBP
并引用与 ESP
寄存器相关的变量。这有两个后果:
- 多一个可用寄存器
- 少了一种调试的可能性(调试时经常使用
EBP
值作为当前Stack Frame的参考来识别局部变量)。因此,如果没有单独的 调试信息文件 . ,这会使调试变得更加困难
所以回答你的主要问题
How does a process keep track of its local variables
进程跟踪它们的栈帧(其中包含局部变量),但不跟踪它们的局部变量变量 本身。 堆栈框架随着每个进程切换而变化。 局部变量只是相对于保存在寄存器EBP
中的堆栈帧指针(或相对于Stack Pointer ESP
, 取决于编译器设置).
编译器完成记忆偏移量的工作。这些偏移量只是硬编码的。就像将变量加载到寄存器(例如加载到 eax)一样,编译器会产生类似 mov eax, [esp-4]
的结果,其中 esp 是堆栈指针寄存器,4 是偏移量。如果新变量将被推送到下一个 mov 到 get/set 变量将有更大的偏移量。这一切都是编译时间分析。
此外,某些平台上的堆栈可能会颠倒 - 因此偏移量将为正。