Skip to content

分段和地址 ​


内存地址类型 ​

地址类型描述
逻辑地址机器语言指令使用的地址,指定一个操作数的地址或一条指令的地址
线性地址线性地址是一个无符号整数,范围0x00~0xffffffff(ffffffff),
由逻辑地址的段基址+段偏移计算得到
若无分页机制,线性地址即物理地址
物理地址内存单元的实际地址,用于芯片级内存单元寻址

地址范围由操作系统上虚拟地址长度和物理地址长度决定

shell
#amd64 ubuntux系统上,查看CPU信息
lscpu 
#Address sizes:         45 bits physical, 48 bits virtual
#可见该系统上。虚拟地址是48bit,物理地址45bit

8086分段 ​

由于8086有20跟地址总线,但是寄存器只有16bit.
无法通过单一寄存器寻址20地址线对应的空间
所以将存储分段,使用段地址+段内偏移的方式寻址.

8080 将内存分为4个段:

  • 数据段
  • 代码段
  • 堆栈段
  • 额外数据段

segment

8086地址计算 ​

段地址左移4位+段内偏移address

CPU工作模式 ​

实地址模式 ​

即早期8086的工作模式,
早期8086有16位数据总线,20位地址总线,
16位地址可寻址64KB地址空间,
在有20位地址线的情况下,使用段地址+段内偏移的方式可寻址1M地址空间. 详细可参见上一节。

保护模式 ​

目前寄存器和地址总线目前已达到32bit/64bit,(主流操作系统已经放弃对32系统的支持)
32bit地址总线即可寻址4GB地址空间,64bit地址总线可寻址更大内存空间
如果使用段地址+段内偏移的方式寻址,每个程序都可以访问整个内存空间
因此,增加保护模式。

保护模式寻址 ​

将段的信息(即段描述符,包含:段基址,长度,属性等)放到段描述符表,
段描述符表又分为全局段描述符表(GDT)和局部段描述符表(LDT)
全局描述符表位于内存中,表的地址放到GDTR寄存器中,
使用16位段选择子(即原来的段寄存器CS/DS/ES/GS的低16位,保护模式下改了用处)从描述符表里选段信息,

段描述符 ​

8字节

字段长度描述
segment limit20bit段长
base address32bit段基地址
type4bit段类型,自行查文档
S1bit0:系统段描述符
1代码段或者数据段描述符
DPL2bit特权级,0为最高特权级,3为最低,表示访问该段时CPU所需处于的最低特权级
P11:有效
0:无效
A
DB
G1bit范围粒度
0:粒度为1字节,20bit可寻址1MB
1:粒度为4KB,20bit可寻址4GB

gdt
gdt

段选择子 ​

gdt

段寻址 ​

Linux64位系统下,段基址都为0
gdt
ldt

分页 ​

寻址

寻址语法 ​

1.立即数寻址 ​

直接使用数字
格式:$number

asm
#将数字1赋值到ax寄存器
movl $1, %eax

2.寄存器寻址 ​

数据直接存寄存器里
格式:%寄存器名字

asm
#%eax直接访问寄存器
movl $1, %eax

3.直接寻址 ​

访问并使用变量

asm
# 定义变量tmp
tmp:
 .int 1
#将tmp的值1赋值到ax寄存器
movl tmp, %eax

4.寄存器间接寻址 ​

寄存器里储存的是变量的地址,通过寄存器访问变量
格式:(%寄存器名)

asm
# 定义变量tmp
tmp:
 .int 1
#将tmp的地址赋值到edi寄存器
move $tmp, %edi
#将立即数1复制到edi存的地址的位置,即tmp置为1
move $1, (%edi)

5.寄存器相对寻址 ​

寄存器里存储的变量的相对地址,通过寄存器加偏移访问变量
格式:偏移量(%寄存器名)

asm
    movl 4(%esp), %eax

6.基址变址寻址方式 ​

格式:base_addrsss(offset_address, index, size)
数值:base_address + offset_address + index * size
其中0值可省略
offset_address和index是寄存器类型

asm
tmp:
.int 1,2,3,4
movl $2, %edi
# 寻址tmp的第3个元素
movl tmp(,edi,4), %eax

参考 ​

Segmentation in Intel x64(IA-32e) architecture - explained using Linux
Linux内存寻址二三事