intel 8086 来说。是一个16位的处理器。他有16位的寄存器。16位的数据总线。20位的地址线。寻址能力1M。因为是16位的寄存器。只有通过 seg:offset的方式。才能寻址1m的地址空间。
到了80386后的处理器。就变成了32位的处理器。寄存器是32位。数据总线也是32.地址也是32位。如果一个寄存器存放的是地址的话。直接寻址能力是4GB。但还是使用了 seg :offset的方式。
在32位的处理器中。仍是采用的是:段+偏移 的寻址方式。但这里的段和实模式下的段有本质的不同。在实模式下,“段”是物理地址的一部分,可以说是段的首地址。但在保护模式下的段。段值只是一个索引。(段值是放在cs ds 等 16位的寄存器中。)他指向一个数据结构的一个表项。在表项中详细定义了段的起始地址。段限长。段的属性的内容。这个数据结构就是 GDT ,LDT 。
GDT的表项有一个固定的名字叫 段描述符。段描述符的格式如下:(共8字节 32位的索引)
| byte7 | byte6| byte5 | byte4 | byte3 | byte2 | byte1 | byte0
段基址(31..24) |段 属 性 |段基址(23.....0) | 段限(0....15)
地址是32位的,所以段基址是四个字节。段的长度是1M。占两个字节。段属性占2个字节(包括段限长的高4位)。
段的属性是:
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
G D/B 0 avl |段限长(19..16)| p | DPl | s |t y p e |
描述GDT的表项的数据结构是 段选择子。段选择子的构成如下:
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
如果ti和rpl都是0.那么索引就是段描述符相对于段表的偏移长度。
例子如下:
mov ax, DESC_VIDIO
mov gs.,ax
mov [gs:edi] ,8 ///[]表明从内存中去数据
第二步:
我们考虑下寻址的具体过程:
如果我们要到内存中取数据。那么 我们得到一个地址 是seg + offset的形式。
cpu的执行过程:
1. cpu通过 ldtr 寄存器知道GDT的具体地址
2. 通过seg 去找段表的该段的描述符表项。找到段的基址
3.然后用段基址+offset 就可以得到数据在内存中 的物理地址。 接下来就是取数据了 。
所以下一步的工作是 把gdt的基址和属性用 lgdt 加载到gdtr寄存器。(因为 段表是在内存中的。而段表的基址存在cpu中。)
gdtr的结构是:
32位 的基址 16位的段界限
然后就是 开 A 20 的地址线。(向下兼容) 置 cr0寄存器的pe 位
最后就是一个 jmp 。进入保护模式 。
注意 :jmp 是一个从16位进入 32位 所以 用到 了
jmp dword 地址
就进入保护模式了
总结下;
准备 GDT
加载 GDT
开 A20
置 CR0
跳转 jmp
我们就进入了