分段和地址
内存地址类型
| 地址类型 | 描述 |
|---|---|
| 逻辑地址 | 机器语言指令使用的地址,指定一个操作数的地址或一条指令的地址 |
| 线性地址 | 线性地址是一个无符号整数,范围0x00~0xffffffff(ffffffff), 由逻辑地址的 段基址+段偏移计算得到若无分页机制,线性地址即物理地址 |
| 物理地址 | 内存单元的实际地址,用于芯片级内存单元寻址 |
地址范围由操作系统上虚拟地址长度和物理地址长度决定
#amd64 ubuntux系统上,查看CPU信息
lscpu
#Address sizes: 45 bits physical, 48 bits virtual
#可见该系统上。虚拟地址是48bit,物理地址45bit8086分段
由于8086有20跟地址总线,但是寄存器只有16bit.
无法通过单一寄存器寻址20地址线对应的空间
所以将存储分段,使用段地址+段内偏移的方式寻址.
8080 将内存分为4个段:
- 数据段
- 代码段
- 堆栈段
- 额外数据段

8086地址计算
段地址左移4位+段内偏移
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 limit | 20bit | 段长 |
| base address | 32bit | 段基地址 |
| type | 4bit | 段类型,自行查文档 |
| S | 1bit | 0:系统段描述符 1代码段或者数据段描述符 |
| DPL | 2bit | 特权级,0为最高特权级,3为最低,表示访问该段时CPU所需处于的最低特权级 |
| P | 1 | 1:有效 0:无效 |
| A | ||
| DB | ||
| G | 1bit | 范围粒度 0:粒度为1字节,20bit可寻址1MB 1:粒度为4KB,20bit可寻址4GB |


段选择子

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

分页

寻址语法
1.立即数寻址
直接使用数字
格式:$number
#将数字1赋值到ax寄存器
movl $1, %eax2.寄存器寻址
数据直接存寄存器里
格式:%寄存器名字
#%eax直接访问寄存器
movl $1, %eax3.直接寻址
访问并使用变量
# 定义变量tmp
tmp:
.int 1
#将tmp的值1赋值到ax寄存器
movl tmp, %eax4.寄存器间接寻址
寄存器里储存的是变量的地址,通过寄存器访问变量
格式:(%寄存器名)
# 定义变量tmp
tmp:
.int 1
#将tmp的地址赋值到edi寄存器
move $tmp, %edi
#将立即数1复制到edi存的地址的位置,即tmp置为1
move $1, (%edi)5.寄存器相对寻址
寄存器里存储的变量的相对地址,通过寄存器加偏移访问变量
格式:偏移量(%寄存器名)
movl 4(%esp), %eax6.基址变址寻址方式
格式:base_addrsss(offset_address, index, size)
数值:base_address + offset_address + index * size
其中0值可省略
offset_address和index是寄存器类型
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内存寻址二三事