您的位置:首页 > 运维架构 > Linux

Linux内核switch_to宏实现进程切换的原理

2015-09-05 22:58 639 查看
switch_to宏的实现:

27 /*
28  * Saving eflags is important. It switches not only IOPL between tasks,
29  * it also protects other tasks from NT leaking through sysenter etc.
30  */
31 #define switch_to(prev, next, last)                                     \
32 do {                                                                    \
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   */ \
45                      "movl %[next_sp],%%esp\n\t"        /* restore ESP   */ \
46                      "movl $1f,%[prev_ip]\n\t"  /* save    EIP   */     \
47                      "pushl %[next_ip]\n\t"     /* restore 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)


关键代码:

44                      "movl %%esp,%[prev_sp]\n\t"


其中,prev_sp的定义为:

55                      : [prev_sp] "=m" (prev->thread.sp)


也即把当前堆栈的值保存到当前进程的task_struct结构的thread结构体中,而

45                      "movl %[next_sp],%%esp\n\t"


则把保存在要被切换到的进程的task_struct结构的thread结构体中的堆栈值,赋值给esp寄存器,即栈顶指针指向了要切换到的进程的内核栈,实现了堆栈的切换。



47                      "pushl %[next_ip]\n\t"
49                      "jmp __switch_to\n"


则实现了cpu执行流的切换,即切换到next执行指令。47行把next_ip,即next->thread.ip的值压入堆栈,然后跳转到__switch_to处执行,由于__switch_to是一个函数,并且是使用的jmp实现的跳转,这样会把刚刚压入堆栈的next->thread.ip值当成返回地址。因此在该函数返回(ret)的时候,cpu会跳转到next->thread.ip指向的指令流执行,从而实现进程切换。

关于寄存器的保存:

该宏的实现中,通过下面的语句,通用寄存器ebx, ecx, edx, esi, edi的值被故意修改了:

60                        "=b" (ebx), "=c" (ecx), "=d" (edx),
61                        "=S" (esi), "=D" (edi)


即不保存和恢复通用寄存器的值,而只保存了和恢复标志寄存器,堆栈寄存器等。因为是在内核态进行切换,所以cs和ds的值无需修改(都是指向内核代码段和数据段)。

那为什么在用户空间到内核空间的切换过程中,通用寄存器的值需要被保存呢?个人以为,因为切换的时候,可能通过寄存器还在使用当中,所以需要进行保存。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: