内核堆栈切换
环境
CPU : x86 32bit
内核版本:2.6.18
进程切换
为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换,任务切换,上下文切换
schedule()——>context_switch()——>switch_to()——>__switch_to()
schedule是主调度函数,当schedule()需要暂停A进程的执行而继续B进程的执行时,就发生了进程之间的切换。
进程切换主要有两部分:
1、切换全局页表项(这个切换工作由context_switch()完成;
2、切换内核堆栈和硬件上下文(__switch_to()主要完成硬件上下文切换,switch_to主要完成内核堆栈切换)。
thread_struct
1.一个进程的硬件上下文主要保存在thread_struct中
2.其他信息放在内核态堆栈中
struct thread_struct {
/* cached TLS descriptors. */
struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
unsigned long esp0;
unsigned long sysenter_cs;
unsigned long eip;
unsigned long esp;
unsigned long fs;
unsigned long gs;
/* Hardware debugging registers */
unsigned long debugreg[8]; /* %%db0-7 debug registers */
/* fault info */
unsigned long cr2, trap_no, error_code;
/* floating point info */
union i387_union i387;
/* virtual 86 mode info */
struct vm86_struct __user * vm86_info;
unsigned long screen_bitmap;
unsigned long v86flags, v86mask, saved_esp0;
unsigned int saved_fs, saved_gs;
/* IO permissions */
unsigned long *io_bitmap_ptr;
unsigned long iopl;
/* max allowed port in the bitmap, in bytes: */
unsigned long io_bitmap_max;
};
内核堆栈切换
switch_to利用了prev,next,last三个参数:
prev:指向当前进程
next:指向被调度的进程
last : 指向当前进程
#define switch_to(prev,next,last) do { \
unsigned long esi,edi; \
//下面两步把EFLAGS和EBP入栈保存现场。
asm volatile("pushfl\n\t" /* Save flags */ \
"pushl %%ebp\n\t" \
//把ESP的值保存到prev->thread.esp,保存当前进程上下文
"movl %%esp,%0\n\t" /* save ESP */ \
//把next->thread.esp值赋值给ESP,堆栈被切换进入到next进程
"movl %5,%%esp\n\t" /* restore ESP */ \
//“$1f”指向lable 1
//在prev进程的上下文设置返回地址,返回到下面标号为1处
//一个进程被正常切换出时,保存的eip总是标号为1的那个位置
//把label 1的地址赋值给prev->thread.eip
"movl $1f,%1\n\t" /* save EIP */ \
//把next->thread.eip的值入栈
//从next进程的上下文中取得该进程的返回地址,放入堆栈中
"pushl %6\n\t" /* restore EIP */ \
//调用__switch_to进行硬件上下文切换
"jmp __switch_to\n" \
//当这个进程再次被调度运行时,恢复在堆栈上的返回地址总是这个1。
"1:\t" \
"popl %%ebp\n\t" \
"popfl" \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip), \
"=a" (last),"=S" (esi),"=D" (edi) \
:"m" (next->thread.esp),"m" (next->thread.eip), \
"2" (prev), "d" (next)); \
} while (0)