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_EAXOriginal 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寄存器及返回地址一同压入堆栈。

·            段寄存器DSES原来的内容入,然后装入内核数据段描述符__KERNEL_DS(定义为0x18,内核段的DPL0

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_ALLSAVE_ALL遥相呼应。当执行到iret指令时,内核又恢复到刚进入中断门时的状态,并使CPU从中断返回。

5.将当前进程的task_struct 结构的地址放在寄存器中

 #define GET_CURRENT(reg) \

         movl $-8192, reg; \

         andl %esp, reg   

    从下一章“task_struct 结构在内存存放”一节我们将知道,当前进程的task_struct存放在内核的底部,因此,以上两条指令就可以把task_struct结构的地址放在reg寄存器中。