Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8665
  • 博文数量: 5
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-28 20:12
个人简介

在 CU 的博客

文章分类

全部博文(5)

文章存档

2015年(5)

我的朋友

分类: LINUX

2015-04-10 22:45:21

内核版本3.19, 架构 x86_64

switch_to() 是在 arch/x86/include/asm/switch_to.h中, 内容如下:

  1. #define switch_to(prev, next, last) \
  2. do { \
  3.          /* \
  4.           * Context-switching clobbers all registers, so we clobber \
  5.           * them explicitly, via unused output variables. \
  6.           * (EAX and EBP is not listed because EBP is saved/restored \
  7.           * explicitly for wchan access and EAX is the return value of \
  8.           * __switch_to()) \
  9.           */ \
  10.          unsigned long ebx, ecx, edx, esi, edi; \
  11.                                                                          \
  12.          asm volatile("pushfl\n\t" /* save flags */ \
  13.                       "pushl %%ebp\n\t" /* save EBP */ \
  14.                       "movl %%esp,%[prev_sp]\n\t" /* save ESP */ \
  15.                       "movl %[next_sp],%%esp\n\t" /* restore ESP */ \
  16.                       "movl $1f,%[prev_ip]\n\t" /* save EIP */ \
  17.                       "pushl %[next_ip]\n\t" /* restore EIP */ \
  18.                       __switch_canary \
  19.                       "jmp __switch_to\n" /* regparm call */ \
  20.                       "1:\t" \
  21.                       "popl %%ebp\n\t" /* restore EBP */ \
  22.                       "popfl\n" /* restore flags */ \
  23.                                                                          \
  24.                       /* output parameters */ \
  25.                       : [prev_sp] "=m" (prev->thread.sp), \
  26.                         [prev_ip] "=m" (prev->thread.ip), \
  27.                         "=a" (last), \
  28.                                                                          \
  29.                         /* clobbered output registers: */ \
  30.                         "=b" (ebx), "=c" (ecx), "=d" (edx), \
  31.                         "=S" (esi), "=D" (edi) \
  32.                                                                          \
  33.                         __switch_canary_oparam \
  34.                                                                          \
  35.                         /* input parameters: */ \
  36.                       : [next_sp] "m" (next->thread.sp), \
  37.                         [next_ip] "m" (next->thread.ip), \
  38.                                                                          \
  39.                         /* regparm parameters for __switch_to(): */ \
  40.                         [prev] "a" (prev), \
  41.                         [next] "d" (next) \
  42.                                                                        \
  43.                         __switch_canary_iparam \
  44.                                                                          \
  45.                       : /* reloaded segment registers */ \
  46.                          "memory"); \
  47.  } while (0)


进程的切换, 最明显的就是栈的切换和内核控制路径的切换, 具体就是对%ebp, %esp 和%eip 的切换. 在 switch_to 中, 显式完成的只有%esp 的切换(movl %[next_sp], %%esp). %ebp 通过切换%esp 之后使用 popl %%ebp 完成, %eip的切换则在 __switch_to()返回执行 return 时隐式完成.

switch_to 中做的事情依次是: 
1. 将 flags 压栈
2. 将 prev 的%ebp 压栈 
3. 将 prev 的%esp 保存到 prev->thread.sp
4. 置%esp 的值为 next->thread.sp, 这样内核栈就切到了 next 的内核栈, 以后的操作都是在 next 的内核栈上做的.
5. 将语句 1: 处的地址保存到 prev->thread.ip
6. 将 next->thread.ip 压栈(注意这里已经是在 next 的栈上操作)

此时 next 的栈上是这样的:

___
%ebp(上次从 next 切换出去时保存的)
___
%eip(刚刚压入) <-%esp 指向这里

而 prev 的栈是这样的:

___
flags
__
%ebp (保存在 prev->thread.sp 中的地址指向这里)

7. 调用 __switch_canary 对 next 的栈做检查
8. 跳转到 __switch_to() 执行. 这里的 trick 是使用 jmp 而不是 call, 避免机器把当前%eip 压栈
9. 在 __switch_to() 执行到 ret 时, 从把栈顶值弹出到%eip 中, 这就完成了内核控制路径的切换.
10. 栈 pop, 弹出值作为%ebp. 这里栈中的值不是这次 switch_to 中保存的, 而是上一次 B 作为 switch_to 的 prev 时保存的.
11. 从栈中恢复 flags.

还有一些细节:
1. 汇编中 output parameters 中有 "=a" (last). 也就是把%eax 的值放入 last 中. __switch_to() 的最后一条语句是 return prev_p; 是将 pre 指针放到%eax 中. 这样就将 next 的 last 指针设为了 prev.

2. 为什么不在 __switch_to() 之前就把%ebp 弹出来? 因为%ebp 没有在 thread_struct 结构中保存, 它只能保存在栈上. %ebp 被保存在栈上的原因是使用 get_wchan() 将会对栈上的%ebp 进行追踪, 找到这个睡眠进程的内核控制流在哪个函数内.(我感觉还是没回答这个问题, 哪天改了试试)

3. __switch_to()中大概做的是切换段寄存器, gdt 之类的事情.

4. 若 next 进程之前没进过 switch_to, 栈上%eip 的位置会是 ret_from_fork().
阅读(641) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:CFS笔记(1)

给主人留下些什么吧!~~