这两个概念在计算机体系结构、计算机原理和接口技术、汇编语言等课程中经常出现,但没有明确的定义和说明,容易混淆。 今天我们来彻底了解一下吧。
1 .从执行实体(进程、线程、中断处理器、内核组件等)的角度来看,有效地址是指可用于指定哪些内存位置可以执行访问操作的值例如:
寄存器中保存有10000的值,如果系统执行了获取寄存器内容指定的存储器位置内容的指令,则该数据获取操作的有效地址为10000。 也就是说,此存储器位置由寄存器的内容确定,并且用于读取数据操作的有效地址为10000。 寄存器中保存有值10000,系统执行指令,将数据保存到比指定位置大的120个位置时,该操作指令的有效地址为10120。从一个稍微不同的角度来看:
如果某个命令获取变量a的内容,则从启动获取操作的执行实体来看,该获取操作的有效地址是该变量的地址。 如果指令将值存储在数组v的第10个元素中,则从启动此存储操作的执行实体来看,此存储操作的有效地址将是数组v的第10个元素的地址。 指令将值保存在指针变量p指定的存储器位置时,有效地址为指针变量p的内容。 请注意最后三个示例中前两位的短语“从启动xx操作的执行实体来看”。 从执行实体的角度来看,多个执行实体可以访问的内存位置的有效地址并不完全相同。 例如,两个进程可能共享一个内存空间。 尽管尽可能避免了这种情况,但参与访问的每个执行实体的有效地址可能不同。 在这种情况下,共享区域的有效地址差异取决于所访问的执行实体。 因此,通常只有从特定执行实体可以访问内存位置的角度来看,内存位置的有效地址才是有意义的。
另一个特别容易引起混淆的地方是,为了区分跨段访问,Intel处理器将在不进行跨段访问的情况下计算的这个地址称为偏移,也称为有效地址。 在段之间访问时计算的该地址称为逻辑地址。 如下图所示。
在实际的APP应用程序开发中,不区分你是如何构成的地址,计算出的地址统称为有效地址。
1.1有效地址和虚拟地址如果发起存储器访问操作的处理器不支持虚拟地址,或者关闭了虚拟地址功能,则该有效地址是处理器访问的存储器的物理地址(即,DRAM、CCD
另一方面,如果启动内存访问操作的处理器支持虚拟内存,并且打开了虚拟内存功能,则处理器的内存映射单元会将该操作的有效地址转换为该操作访问的特定内存的物理地址。
1.2为什么会有“有效地址”这个术语“有效地址”的存在,是为了毫不含糊地描述上述例子中所表达的概念而创造的术语。
术语“有效地址”的创建方式几乎可以替换更常见和更容易混淆的“虚拟地址”。 “虚拟地址”这个术语本身在“创建”的意义上可能不会混淆,但其使用已经混淆,至少在过去的30年中,硬件工程师和软件开发人员混淆使用了这个术语(有效)
例如,让我们看一下“虚拟地址”的定义。
可以通过虚拟地址映射设备转换为物理地址的地址。
在系统的虚拟地址映射功能接通时,虚拟地址“虚拟地址”和“有效地址”的定义本质相同。 但是,有效的地址定义实际上包含了虚拟地址的定义。
IBM官方文档中提供了“虚拟地址”的另一个定义,说明了PowerPC处理器如何将有效地址映射到物理地址。 简单来说,如果32位PowerPC处理器或64位PowerPC在32位模式下运行,则32位有效地址将首先转换为52位地址,然后再转换为物理地址。 此物理地址的大小取决于所使用的PowerPC处理器的版本。
描述此转换如何工作的IBM官方文档使用了表示中间52位地址的术语“虚拟地址”。 也就是说,在PowerPC处理器的上下文中,IBM定义的“虚拟地址”和他们定义的“有效地址”的定义本质上是不兼容的(IBM定义“有效地址”是因为许多其他硬件供应商另一方面,IBM在PowerPC上下文中定义的“有效”地址和
面文章开头定义的“有效地址”等效。原则很简单:不管过去这个概念有没有混淆,这个“虚拟地址”在现在的虚拟内存上下文中已经混淆了。
应当指出,即使术语“虚拟地址”不混淆,也仍然需要术语“有效地址”,因为在处理器不支持虚拟内存或者虚拟内存特殊征禁用的上下文环境中,“虚拟地址”变得没有意义。
在很多软硬件实现中,一块内存在单个进程的进程内存空间的多个地方“可见”,情况就变得更为复杂。
通过下面的例子来解释这种情况:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
main()
{
int shmid;
void *addr1, *addr2;
/* Allocate a shared memory segment of at least 100 bytes */
shmid = shmget( IPC_PRIVATE, 100, IPC_CREAT | 0600 );
if ( shmid == -1 ) {
perror("shmget failed");
exit(1);
}
/*
* Attach the shared memory segment at an arbitrary location
* in our address space
*/
addr1 = shmat( shmid, NULL, 0 );
if ( ((long)addr1) == -1 ) {
perror("shmat 1 failed");
exit(1);
}
/*
* Attach the same shared memory segment again at an arbitrary
* location in our address space
*/
addr2 = shmat( shmid, NULL, 0 );
if ( ((long)addr2) == -1 ) {
perror("shmat 2 failed");
exit(1);
}
/*
* Did we get two different locations?
*/
if ( addr1 == addr2 ) {
printf("addr1 is equal to addr2 (0x%p)n",addr1);
} else {
printf("addr1 is 0x%p and addr2 is 0x%pn",addr1,addr2);
strcpy((char *)addr1,"one");
strcpy((char *)addr2,"two");
printf("value at addr1 is "%s"n",addr1);
}
exit(0);
}
程序的输出形如:
addr1 is 0x0x40017000 and addr2 is 0x0x40018000
value at addr1 is "two"
addr2所引用的内存地址的值被addr1所引用的内存地址的值覆盖,这个事实表明,从进程的角度来讲,与shmid 关联的共享内存段有两个不同的有效地址。
如果这个输了形如以下:
addr1 is equal to addr2 (0x40017000)
那么两次系统调用shmat 底层操作系统应该返回相同的地址。
如果这个输出形如以下:
addr1 is 0x0x40017000 and addr2 is 0x0x40018000
value at addr1 is "one"
同样也很奇怪。
1.3 总结有效地址和虚拟地址从以上两点讲解我们可以大致地得出结论:有效地址的术语比虚拟地址术语的应用更广泛,更少的歧义,甚至可以代替虚拟地址使用。我们在应用程序开发中进程所见地址,我们都可以认为是有效地址。
2. 什么是有逻辑地址这个概念的出现,也很容易引起混淆。下面来看看它是如何定义的。
常见的定义:
逻辑地址是程序运行时由CPU生成的地址,它是一个虚拟地址,之所以说是虚拟地址,是因为它不是物理上真实存在的地址。
看看这个定义,又扯上虚拟地址了,这就是多种概念容易引起混淆的地方。从定义来看,通常情况下我们说的逻辑地址,就是相对于物理地址而言的由处理器生成的地址,由前面对有效地址的定义,我们可以认为,这个逻辑地址,可以认为就是有效地址。
同样,这里需要特别注意,在Intel处理器中指的逻辑地址,是指跨段访问时段选择子加上段偏移构成的这个地址,见上面图中所示。