陈良 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://www.xuetangx.com/courses/course-v1:ustcX+USTC001+_/about
1. 使用gdb跟踪schedule()函数
2. 跟踪schedule()函数
3. 跟踪__schedule()内部的context_switch()函数
4. 由于switch_to不是函数,无法设置断点,我们进入内核源码查看context_switch()函数内部
2335 static inline void
2336 context_switch(struct rq *rq, struct task_struct *prev,
2337 struct task_struct *next)
2371 context_tracking_task_switch(prev, next);
2372 /* Here we just switch the register state and the stack. */
2373 switch_to(prev, next, prev);
//分析switch_to宏
31#define switch_to(prev, next, last) \
32do { \
33 /* \
34 * Context-switching clobbers all registers, so we clobber \
35 * them explicitly, via unused output variables. \
36 * (EAX and EBP is not listed because EBP is saved/restored \
37 * explicitly for wchan access and EAX is the return value of \
38 * __switch_to()) \
39 */ \
40 unsigned long ebx, ecx, edx, esi, edi; \
41 \
42 asm volatile("pushfl\n\t" /* save flags */ \
43 "pushl %%ebp\n\t" /* save EBP */ \
44 "movl %%esp,%[prev_sp]\n\t" /* save ESP */ \ //将当前栈顶存储到prev_sp寄存器
45 "movl %[next_sp],%%esp\n\t" /* restore ESP */ \ //将下一个进程的esp,next_esp赋予esp寄存器,用新进程的esp替换了当前进程的esp寄存器
46 "movl $1f,%[prev_ip]\n\t" /* save EIP */ \// 保存当前进程的eip
47 "pushl %[next_ip]\n\t" /* restore EIP */ \ //将下一个进程的eip压入栈顶,使得pop出栈顶的eip为新进程的eip,完成进程切换
48 __switch_canary \
49 "jmp __switch_to\n" /* regparm call */ \
50 "1:\t" \
51 "popl %%ebp\n\t" /* restore EBP */ \
52 "popfl\n" /* restore flags */ \
53 \
54 /* output parameters */ \
55 : [prev_sp] "=m" (prev->thread.sp), \
56 [prev_ip] "=m" (prev->thread.ip), \
57 "=a" (last), \
58 \
59 /* clobbered output registers: */ \
60 "=b" (ebx), "=c" (ecx), "=d" (edx), \
61 "=S" (esi), "=D" (edi) \
62 \
63 __switch_canary_oparam \
64 \
65 /* input parameters: */ \
66 : [next_sp] "m" (next->thread.sp), \
67 [next_ip] "m" (next->thread.ip), \
68 \
69 /* regparm parameters for __switch_to(): */ \
70 [prev] "a" (prev), \
71 [next] "d" (next) \
72 \
73 __switch_canary_iparam \
74 \
75 : /* reloaded segment registers */ \
76 "memory"); \
77} while (0)
//以下4行为核心代码
44 "movl %%esp,%[prev_sp]\n\t" /* save ESP */ \ //将当前栈顶存储到prev_sp寄存器
45 "movl %[next_sp],%%esp\n\t" /* restore ESP */ \ //将下一个进程的esp,next_esp赋予esp寄存器,用新进程的esp替换了当前进程的esp寄存器
46 "movl $1f,%[prev_ip]\n\t" /* save EIP */ \ //保存当前进程的eip
47 "pushl %[next_ip]\n\t" /* restore EIP */ \ //将下一个进程的eip压入栈顶,使得pop出栈顶的eip为新进程的eip,完成进程切换
//以上4行核心代码完成了进程的切换
5. 总结
一般执行过程(用户进程x切换到用户进程y)
(1)中断,保存x用户堆栈,载入x内核堆栈,进入x内核态
(2)内核态下运行schedule,使用switch_to进行进程上下文切换
(3)进入y内核态
(4)恢复现场,返回y用户态
(5)结束