【问题标题】:x86 - Does C uses virtual or linear addresses?x86 - C 使用虚拟地址还是线性地址?
【发布时间】:2014-05-21 12:54:40
【问题描述】:

假设您处于 x86 保护模式,因此启用了分段。

考虑这段代码:

// main
int stackvar, *ptr;

// ptr may contain the address of a variable located in the stack segment
ptr = &stackvar;

// ptr may contain the address of a variable in the heap (data segment)
ptr = (int *) malloc(sizeof(int));

我读到ptr 只包含偏移量(即虚拟地址),不包含段的指示。这意味着它应该与段基地址结合以获得线性地址。是真的吗?

// let %eax = ptr
asm("movl (%eax), %ebx"); // which segment is used by this instruction?

如果%eax 只包含偏移量,而不包含段的指示,机器如何知道应该应用哪个段基地址(例如数据或堆栈段基地址)?

【问题讨论】:

  • printf("%p\n", (void*)ptr);
  • 286操作模式被广泛忽视。用操作系统的名称和使用它的编译器来招待我们。
  • 也许添加一个编译器/OS标签,这肯定不是标准C规定的。
  • @HansPassant - OS2/Warp 和适当的编译器不是答案吗?一眼看去,也许 EMX 作为编译器。
  • 关于默认段寄存器的知识是在处理器中硬编码的。段寄存器也适用于其他保护模式,但它们包含的不是线性地址的一部分,而是描述符表 (e&oe) 中的一个条目。

标签: c assembly x86 memory-address


【解决方案1】:

如果不使用显式段覆盖,则许多操作码都附加了隐式段 - 请参阅汇编器手册。

【讨论】:

  • 在这种情况下,段覆盖是否可能被存储在 ptr 变量中?我的意思是,我已经调试了上面的代码,ptr 得到了以下值:0xbffffcf8(堆栈变量地址)和 0x804a008(堆变量地址)。它们是否包含段指示或只是虚拟地址?如果它们是虚拟地址,我想 - 如果我错了,请告诉我 - 编译器有责任在指令中指定段覆盖(如果隐式段不是你想要的)。
【解决方案2】:

您混淆了 2 个不同的概念:偏移量与虚拟地址。

除非您是操作系统(或设备驱动程序),否则受保护模式操作系统中的每个进程都会获得自己的地址空间。当我这么说的时候,我并不是说他们得到了分配给他们的~4gig 地址空间的一部分。我的意思是 x86 中的每个进程都有自己的 ~4gig 地址空间。

在这种环境下,完全有可能同时运行的两个完全不相关的应用程序都可以调用malloc,并且都返回相同的地址。但是,这并不意味着它们指向相同的数据。来自 process1 的地址 0x804a008 与来自 process2 的地址 0x804a008 没有任何关系。

当进程尝试访问该地址时,处理器内部会发生魔法(请参阅TLBs)将进程的“地址”转换为物理内存位置,从而导致每个进程读取完全不同的内存位过程。这不是您在程序集转储中看到的内容。每次保护模式应用程序访问内存时,处理器都会执行此操作。

所以每个进程都有自己的线性地址空间,而ptr真正指向的地址位置。当您说“ptr 仅包含偏移量(即虚拟地址)”时,这是不正确的。它包含虚拟地址,句点。它不会偏离任何东西。

在 16 位的黑暗日子里,确实存在分段寻址的概念。更重要的是,所有进程都直接访问物理内存并且有相互踩踏的趋势。除非我误解了您的问题或您的(未指定)平台,否则该 malloc 根本不是这样。

对于那里的 kibitzers,是的,我正在简化。人们可以(并且已经)写了整本关于这些东西的书。

【讨论】:

  • 是的,我已经知道这一点,因为我目前正在大学学习“操作系统”课程。虚拟地址不是相对于段的偏移量吗?是的。我的问题更实际。 IE。虚拟地址可能引用不同的段(在同一进程内):如果您只记住变量中的虚拟地址,您如何跟踪该地址所指的段?我的意思是,取消引用ptr 可以成功地处理堆栈和数据地址,那么,C 如何跟踪相关段(如果这不在变量本身中)?
  • 对于任何人来说,最难做的事情之一就是质疑他们的基本假设,但这就是您需要在这里做的。如果您要搁置继续断言“必须”在某个地方存在一个细分市场,那么您的其余问题都会消失,不是吗?如果没有任何段,则不需要保存(或出现在 asm 转储中)。然后,您发布的汇编程序转储非常有意义。一个进程获得 4gig。它用于代码或堆栈或堆,并且操作系统声明块,但 4gig 是你得到的。单个 32 位寄存器足以引用其中任何一个,不需要 seg。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-15
  • 1970-01-01
  • 2017-05-16
  • 2011-06-20
  • 2011-08-20
相关资源
最近更新 更多