看内核之前需要了解一些体系结构相关的知识,本文介绍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的一些内部状态。
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中。
参考:
- 《Intel® 64 and IA-32 Architectures Software Developer’s Manual
Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4》 - 《Understanding the Linux Kernel 3rd Edition》