核心版本:2.6.33.4
switch_to宏定義在System.h (arch\x86\include\asm)中,全文如下:
/*
* Saving eflags is important. It switches not only IOPL between tasks,
* it also protects other tasks from NT leaking through sysenter etc.
*/
#define switch_to(prev, next, last) \
do { \
/* \
* Context-switching clobbers all registers, so we clobber
\
* them explicitly, via unused output variables.
\
* (EAX and EBP is not listed because EBP is saved/restored
\
* explicitly for wchan access and EAX is the return value of
\
* __switch_to())
\
*/ \
unsigned long ebx, ecx, edx, esi, edi;
\
\
asm volatile("pushfl\n\t"
/* save flags */ \
"pushl %%ebp\n\t"
/* save EBP */ \
"movl %%esp,%[prev_sp]\n\t"
/* save ESP */ \
"movl %[next_sp],%%esp\n\t"
/* restore ESP */ \
"movl $1f,%[prev_ip]\n\t"
/* save EIP */ \
"pushl %[next_ip]\n\t"
/* restore EIP 在這條指令把66行跳轉的返回地址即67行的地址壓入棧中,_switch_to函數的最後一條指令ret即可將其彈出到eip中,當這個進程在被切換出去的時候由另外一個進程在執行63行的指令時填入*/
\
__switch_canary
\
"jmp __switch_to\n"
/* regparm call */ \
"1:\t"
\
"popl %%ebp\n\t"
/* restore EBP */ \
"popfl\n"
/* restore flags */ \
\
/* output parameters */
\
: [prev_sp] "=m" (prev->thread.sp),
\
[prev_ip] "=m" (prev->thread.ip),
\
"=a" (last),
\
\
/* clobbered output registers: */
\
"=b" (ebx), "=c" (ecx), "=d" (edx),
\
"=S" (esi), "=D" (edi)
\
\
__switch_canary_oparam
\
\
/* input parameters: */
\
: [next_sp] "m" (next->thread.sp),
\
[next_ip] "m" (next->thread.ip),
\
\
/* regparm parameters for __switch_to(): */
\
[prev] "a" (prev),
\
[next] "d" (next)
\
\
__switch_canary_iparam
\
\
: /* reloaded segment registers */
\
"memory");
\
} while (0)
宏有三個變數:prev, next, last,分別指前一個進程描述符,下一個要切換的進程描述符,上一個進程描述符
首先定義了五個無符號長整形變數ebx, ecx, edx, esi, edi; 通過下面的輸出變數部分
"=b" (ebx), "=c" (ecx), "=d" (edx),
\
"=S" (esi), "=D" (edi)
可以看出這五個變數分別對應通用寄存器ebx, ecx, edx, esi, edi。
由輸入部分的變數
[prev] "a" (prev),
\
[next] "d" (next)
\
我們可以看到prev和next變數分別存在寄存器eax和edx中的
由輸出部分的變數
"=a" (last)
我們可以看到last變數分別存在寄存器eax中的,即last和prev其實是一個值
下面的彙編部分
"pushfl\n\t"
/* save flags */ \
"pushl %%ebp\n\t"
/* save EBP */
把Eflags寄存器和ebp寄存器的值壓入棧中,即prev進程的棧中,當前的esp應該是指向prev的進程棧中
通過輸出參數部分的定義我們可以得知[prev_sp]和[prev_ip]分別對應 (prev->thread.sp)和(prev->thread.ip)的記憶體位址,[next_sp]和[next_ip]的定義也是類似的。
"movl %%esp,%[prev_sp]\n\t"指令的作用是把esp寄存器的值存入prev->thread.sp變數中;
同理,"movl %[next_sp],%%esp\n\t"是把next->thread.sp變數的值移入esp寄存器中,這條指令即完成了進程堆棧的切換,因為esp指向不同進程的堆棧意味著當前在運行不同的進程。
"movl $1f,%[prev_ip]\n\t"指令的作用是把下面的標誌為1的地址存入prev->thread.ip變數的記憶體中
"pushl %[next_ip]\n\t"表示把next進程的next->thread.ip變數的值壓入棧中由上一條指令我們可以得知,在一般的情況下,next->thread.ip的值即為下面標誌為1的地址(next->thread.ip的值為何是下面標誌為1的地址,因為next進程在前一次被切換出去的時候是prev進程,由movl $1f,%[prev_ip]指令的作用是把標誌為1的地址存入prev->thread.ip變數的記憶體中),將此值壓入棧中主要是為了在下面"jmp
__switch_to\n"指令跳到__switch_to函數後,__switch_to函數最後會運行ret指令,此指令會把當時的棧頂值移入eip寄存器中,然後再運行;而我們希望在__switch_to函數後啟動並執行下一條指令的地址就是標誌為1的地址,故在這裡需要這麼做。
__switch_canary的作用現在還不是很明白,不過指令還是很簡單的
標誌為1的地址處啟動並執行兩條指令為
"popl %%ebp\n\t"
/* restore EBP */ \
"popfl\n"
/* restore flags */ \
即對應最上面的
"pushfl\n\t"
/* save flags */ \
"pushl %%ebp\n\t"
/* save EBP */ \