Linux2.6 使用了软件来实现进程切换,忽略了Intel X86架构提供的硬件切换机制。
为什么要使用软件切换?
1)可控制性更大。硬件切换仅仅用jump far即可,而软件切换需要一系列的判断和mov
2)性能差不多。软件可优化的余地更大。
进程切换发生在switch()函数中。切换主要包括用户空间(pgd)和运行上下文(hardware context和堆栈等)的切换。
这里要谈的switch_to就是实现运行上下文的切换的。
#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 */ \
//压入堆栈,因为下面调用的是jmp,而在__switch_to中采用ret返回,则返回地址就变成了[next_ip],也就是进程next的标号为1处,进入next执行。
"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), \ //将eax写入到last中
\
/* clobbered output registers: */ \
"=b" (ebx), "=c" (ecx), "=d" (edx), \
"=S" (esi), "=D" (edi) \
\
/* input parameters: */ \
: [next_sp] "m" (next->thread.sp), \
[next_ip] "m" (next->thread.ip), \
\
/* regparm parameters for __switch_to(): */ \
[prev] "a" (prev), \ //将prev读入到eax中
[next] "d" (next)); \
} while (0)
在schedule中经常这样调用
switch_to(prev,next,prev)
设想这样一种情景:
A切换到B,则切换完成后。A的内核堆栈中
prev=A,next=B
现从进程C切换到进程A:
切换前:
C: prev=C,next=A
A: prev=A,next=B
切换(C的内核堆栈):
prev -> eax eax中存放的是C
切换完成(A的进程堆栈)
要将eax值写入prev,得:
C:prev=C,next=A
A:prev=C,next=B
prev,next都是schedule中的局部变量。进程总是在schedule中让出处理器。切换后新进程运行的第一条指令总是:
"1:\t" \
"popl %%ebp\n\t" /* restore EBP */ \
"popfl\n" /* restore flags */ \
因此prev,next在进程sleep的时候会在内核堆栈中保持
文章出处:
阅读(633) | 评论(0) | 转发(0) |