0%

IA32常用寄存器

看内核之前需要了解一些体系结构相关的知识,本文介绍IA32常用的几个寄存器。
- 通用寄存器:EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP
- 段寄存器:CS,DS,ES,FS,GS,SS
- EFLAGES寄存器
- EIP寄存器
- 控制寄存器:CR0,CR1,CR2,CR3,CR4
- GDTR,LDTR,IDTR寄存器
- TR寄存器

1. 通用寄存器

用于存放:
  1. 算数和逻辑操作的操作数
  2. 地址操作的操作数
  3. 内存指针
这些寄存器通常可以存放任何东西,但有时候会用作特定用途,比如:
  EAX——存放累加操作数和结果,传递参数和结果等
  EBX——指向DS数据段中数据的指针
  ECX——字符串和循环操作的计数器
  EDX——I/O指针
  ESI——指向DS数据段中数据的指针;字符串操作的源指针
  EDI——跟ES配合,指向目的数据的指针;字符串操作的目的指针
  ESP——栈指针(在SS段中)
  EBP——指向栈中的数据(SS段中),一般用作函数嵌套调用时的栈帧基址

2. 段寄存器

当用平坦内存模型(flat memory model)的时候,段寄存器都指向0,不分段。
当用段内存模型(segmented memory model)的时候,段寄存器指向各自的段,且有CPU的保护机制。
保护模式下(段内存模型):
  CS:保存当前指向代码段的段选择子,配合EIP,可以找到下一个要执行的代码。
  DS,ES,FS,GS:保存四个数据段的段选择子,可以让当前任务同时获取四个数据段,比如一个指向当前特权级的数据段,另一个指向更高特权级的数据段,第三个指向动态创建的数据结构,第四个指向跟另一个程序共享的数据段。
  SS:保存当前栈的段选择子。
段寄存器中保存着段选择子,根据该选择子可以选择GDTR(或LDTR)指向的GDT(或LDT)表中的某一项(段描述符),然后根据该项可以找到相应的段(选择的时候CPU可以进行权限检查等)。如果CPU每次都从段寄存器拿到段选择子,再根据GDTR找到相应的项,再找到段就太麻烦了,所以这几个段寄存器都有个隐藏的寄存器,不暴露给开发人员,只是CPU内部使用的,用于缓存段地址,加快处理速度。如下图:
段寄存器

3. EFLAGS

  存储CPU的一些内部状态。
EFLAGS寄存器

4. EIP寄存器

  用于存放下一个要执行的代码的地址,非常重要,通过特定的指令(JMP,Jcc,CALL,RET,IRET)可以改变值,不能被直接读取,只能在call调用的时候从栈中读取,可以直接赋值,然后用RET或IRET跳转(内核用于向高特权级翻转)。

5. 控制寄存器

控制寄存器
CR0——包含控制处理器操作模式和状态的标志
CR1——保留
CR2——包含缺页中断时的线性地址
CR3——包含分页的第一层结构的基址和两个标志位(PCD,PWT)
CR4——包含一些架构扩展,指定特定的处理器兼容。
一些位解释如下:
PG: Paging,是否分页
CD:Cache Disable,是否使用物理缓存
PE:Protection Enable,打开保护模式,该标志位不会启动分页,只会启动基于段的保护(分页和段都是保护,其实是重复的,linux只用了分页保护)。

6. GDTR,LDTR,IDTR

存放GDT,LDT,IDT的基址。

GDT:Global Descriptor Table,全局描述符表,存放全局的段描述符的数组,只有1份,通过该表可以找到所有的段,也只有通过该表才能实现处理器基于段的保护。

LDT:Local Descriptor Table,局部描述符表,存放局部的段描述符的数组,可以有多份,当前的LDT基址存放在LDTR中。另外,每一份LDT都要在GDT中有一个描述符(说明一个LDT本身也是内存中的一个段)。通过LDT可以获取当前任务的相关段。

IDT:Interrupt Descriptor Table,中断描述符,存放处理器的各个中断的地方。

7. TR寄存器

存放当前TSS的基址。
CPU处理多任务的时候,需要在各个任务之间切换,保存当前任务状态,加载下一个任务的状态,然后执行下一个任务,状态保存在多个地方,其中栈的相关信息保存在TSS中。


参考:

  1. 《Intel® 64 and IA-32 Architectures Software Developer’s Manual
    Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4》
  2. 《Understanding the Linux Kernel 3rd Edition》