我知道当我编译一个程序然后用 objdump 检查时,我有地址。这些是相对地址。
但是如果它是一个 C 程序,并且我使用 %p 来 printf 一个指针,那就是一个 虚拟/逻辑地址。谁产生这个?那是装载机吗?
如果是,为什么我需要加载程序来执行此操作? 一个相对地址(由编译器生成),然后MMU只需添加程序在实内存中的基址偏移量即可到达物理地址,这还不够吗?为什么需要虚拟地址而相对地址不够?
此外,虚拟地址应该用来创建一种假象,使进程成为内存中唯一的一个并且连续分配。我可以用相对(由编译器生成)地址来想象这一点,但我无法用虚拟/逻辑(由加载器生成)地址来想象......加载器如何实现将程序代码放入虚拟内存的连续部分?虚拟内存不是被其他程序在不同的部分使用吗?或者是否会发生两个完全不同的程序具有相同的虚拟/逻辑地址?
另外,最后一个疑问:为什么网上很多地方都写着“虚拟/逻辑地址是由CPU生成的”.....这不是真的.....它是由操作系统完成的,所以为什么这么写到处?我错过了什么吗?
案例一:
链接器定义进程的初始状态。如果您打印来自链接器的静态位置的地址。
案例二:
由于共享库(也由链接器创建)通常可以加载到不同的位置,因此这可能会变得复杂。在这种情况下,您的地址是由链接器(偏移量)和加载器的组合定义的。
为了安全起见,某些操作系统会在不同位置加载程序,这也可能会让情况变得复杂。在这种情况下,您的地址是由链接器和加载器的组合定义的。链接
正是出于这两个原因,我们努力寻求不依赖于特定内存位置的“位置无关代码”。
但是,在某些情况下,需要特定的内存位置或来自位置不独立的代码。
静态 int a, *b=&a ;这里“b”的初始化需要知道“a”的位置。一些系统对此有解决方法,允许加载器在加载时初始化“a”,以便代码仍然可以独立于位置。
案例三:
如果要打印动态内存的位置,该位置通常来自运行时的操作系统。
int *x = malloc(sizeof(*x));案例四:
如果要打印堆栈的位置,则该位置是在运行时通过备份确定的,该备份可以在运行时来自链接器或操作系统。在系统中,链接器定义堆栈。在其他情况下,操作系统定义堆栈。