3.4.3 与堆栈有关的常量、数据结构及宏
在中断、异常及系统调用的处理中,涉及一些相关的常量、数据结构及宏,在此先给予介绍(大部分代码在arch/i386/kernel/entry.S中)。
1. 常量定义
下面这些常量定义了进入中断处理程序时,相关寄存器与堆栈指针(ESP)的相对位置,图3.6给出了在相应位置上所保存的寄存器内容:
EBX = 0x00
ECX= 0x04
EDX= 0x08
ESI= 0x0C
EDI= 0x10
EB = 0x14
EAX= 0x18
DS= 0x1C
ES = 0x20
ORIG_EAX = 0x24
EIP = 0x28
CS = 0x2C
EFLAGS = 0x30
OLDESP= 0x34
OLDSS = 0x38
+0x38 用户栈的SS
+0x34 用户栈的ESP 用户堆栈指针
+0x30 EFLAGS
+0x2C 用户空间的CS
+0x28 EIP 返回到用户态的地址
+0x24 ORIG_EAX
+0x20 ES
+0x1C DS
+0x18 EAX
+0x14 EBP
+0x10 EDI
+0XC ESI
+8 EDX
内核堆栈指针ESP +4 ECX
0 EBX
图3.6 进入中断理程序时内核堆栈示意图
其中,ORIG_EAX是Original eax之意,其具体含义将在后面介绍。
2.存放在栈中的寄存器结构pt_regs
在内核中,很多函数的参数是pt_regs数据结构,定义在include/i386/ptrace.h中:
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int xds;
int xes;
long orig_eax;
long eip;
int xcs;
long eflags;
long esp;
int xss;
};
把这个结构与内核栈的内容相比较,会发现堆栈的内容是这个数据结构的一个映象。
3.保存现场的宏SAVE_ALL
在中断发生前夕,要把所有相关寄存器的内容都保存在堆栈中,这是通过SAVE_ALL宏完成的:
#define SAVE_ALL /
cld; /
pushl %es; /
pushl %ds; /
pushl %eax; /
pushl %ebp; /
pushl %edi; /
pushl %esi; /
pushl %edx; /
pushl %ecx; /
pushl %ebx; /
movl $(__KERNEL_DS),%edx; /
movl %edx,%ds; /
movl %edx,%es;
该宏执行以后,堆栈内容如图3.6所示。把这个宏与图3.5 结合起来就很容易理解图3.6,在此对该宏再给予解释:
· CPU在进入中断处理程序时自动将用户栈指针(如果更换堆栈)、EFLAGS寄存器及返回地址一同压入堆栈。
· 段寄存器DS和ES原来的内容入栈,然后装入内核数据段描述符__KERNEL_DS(定义为0x18),内核段的DPL为0。
4.恢复现场的宏RESTORE_ALL
当从中断返回时,恢复相关寄存器的内容,这是通过RESTORE_ALL宏完成的:
#define RESTORE_ALL /
popl %ebx; /
popl %ecx; /
popl %edx; /
popl %esi; /
popl %edi; /
popl %ebp; /
popl %eax; /
1: popl %ds; /
2: popl %es; /
addl $4,%esp; /
3: iret;
可以看出,RESTORE_ALL与SAVE_ALL遥相呼应。当执行到iret指令时,内核栈又恢复到刚进入中断门时的状态,并使CPU从中断返回。
5.将当前进程的task_struct 结构的地址放在寄存器中
#define GET_CURRENT(reg) /
movl $-8192, reg; /
andl %esp, reg
从下一章“task_struct 结构在内存存放”一节我们将知道,当前进程的task_struct存放在内核栈的底部,因此,以上两条指令就可以把task_struct结构的地址放在reg寄存器中。