先说结论:
这几种地址间的关系:
- 逻辑地址经过分段单元转换成线性地址。线性地址通过分页单元转换成物理地址。
- 虚拟地址是软件(内核和应用程序)使用的地址,它等于逻辑地址中的段偏移。
- 段基址(base address) + 段偏移(offset) = 线性地址。Linux不适用分段管理模型,所有的段基址都是0x00000000。所以0 + 段偏移 = 线性地址,所以Linux中虚拟地址=线性地址。
接下来我们挨个讲解每种地址。
1. 物理地址
参考:Intel® 64 and IA-32 Architectures.pdf Page 70: Volume 1: 3.3 MEMORY ORGANIZATION
定义:CPU给系统上所有的存储空间(内存、显存、IO设备的存储等等)编了地址,编号从0开始一直到可用物理内存的最高端。叫物理地址。CPU就用这个地址,通过地址总线访问系统中的内存。
两种物理地址编址方式:
- 独立编址:物理地址 = 内存, IO空间有独立的IO地址。CPU通过IN、OUT指令+IO地址访问IO的寄存器和存储空间
- 统一编制:物理地址 = 内存 + IO空间。CPU通过物理地址像访问内存一样访问IO空间。
三种物理地址管理模型:
- Flat Model(Linux用的这一种)
- Segmented Model:分段模式的主要优势是提高可靠性,内核内存和app内存可以安置在不同的segment。
- Real-Address Mode Model:实地址模式主要为了兼容Intel 8086处理器,把线性的物理地址平等的分成size相同的segment。
2. 线性地址
- 分段模式下,逻辑地址经过段分段单元处理后得到线性地址。
- 如果启用了分页机制,那么线性地址可以再经过变换产生物理地址。若是没有采用分页机制,那么线性地址就是物理地址。
- 段基址+offset =线性地址
- 线性地址经过分页机制转换成物理地址。
3. 逻辑地址
在分段模式下,逻辑地址= Selector+offset 这种形式。
当程序想要通过逻辑地址访问物理内存:
1. 逻辑地址的selector填到CPU对应的段寄存器中,offset填到CPU的EIP寄存器中。
PS:CPU有多个段寄存器(CS、DS、SS、ES等),这里以CS为例。
2. CPU根据CS(段寄存器)中的selector中的index字段,到GDT中找到第index个段描述符。
3. 解析段描述符,从段描述符里解析出段基址(每个段的起点地址)。
4. 段基址+EIP中的offset得到线性地址。
5. 线性地址通过分页机制处理后得到物理地址。
6. CPU使用物理地址通过地址总线访问物理内存。
- Linux不使用分段,所以给所有的segment基地址都填为0。
- 虚拟地址 = 逻辑地址中的offset, 0 + offset = 线性地址。所以Linux中,虚拟地址 = 线性地址
- selector 是 CS 寄存器的值,offset 是 EIP 寄存器的值。
- CS寄存器的RPL代表了当前CPU的特权级(ring 0-3)
4. 虚拟地址
虚拟地址是给代码用的。
因为Linux中不适用内存分段模式,所以给各个segment的基地址都填0.
所以在Linux中:虚拟地址=线性地址≈逻辑地址