分享

switch_to

 Liucw2012 2012-04-26

switch_to

分类: Process Scheduling1257人阅读评论(2)收藏举报
switch_to()负责从上一个进程的处理器状态切换到新进程的处理器状态

15 #define switch_to(prev,next,last) do {                                  
16         unsigned long esi,edi;                                          
17         asm volatile("pushfl "                                       
18                      "pushl %%ebp "                                  
19                      "movl %%esp,%0 "        /* save ESP */          
20                      "movl %5,%%esp "        /* restore ESP */       
21                      "movl $1f,%1 "          /* save EIP */          
22                      "pushl %6 "             /* restore EIP */       
23                      "jmp __switch_to "                                
24                      "1: "                                             
25                          "popl %%ebp"                                   
26                      "popfl"                                            
27                      :"=m" (prev->thread.esp),"=m" (prev->thread.eip),  
28                       "=a" (last),"=S" (esi),"=D" (edi)                 
29                      :"m" (next->thread.esp),"m" (next->thread.eip),    
30                       "2" (prev), "d" (next));                          
31 } while (0)

    • 输出部分有5个参数,表明这段程序执行后有5项数据会有改变。其中%0和%1都在内存中,分别为prev->thread.esp和prev->thread.eip,而%2则与寄存器EAX结合,对应于参数中的last. [输出的参数为5个,分别为prev->thraad.esp(%0), prev->thread.eip(%1), last(%2), esi(%3), edi(%4) ]
    • 输入部分有4个参数。其中%5和%6在内存中,分别为next->thread.esp和next->thread.eip;%7与寄存器EAX结合,对应于prev;%8与寄存器EDX结合,对应于next [输入的参数为4个,分别为next->thread.esp(%5), next->thread.eip(%6), prev(%2), next(%7) ]
    • "2" (prev), "d" (next),在寄存器%%eax和%%edx中分别保存prev和next的值
    • 第一步,pushfl, pushl %%ebp, 将进程prev的eflags和ebp保存到该进程的内核堆栈中
    • 第二步,movl %%esp, %0(prev->thread.esp),将当前进程prev的系统空间堆栈指针存入该进程的进程描述符结构体中(prev->thread.esp).
    • 第三步,movl %5(next->thread.esp), %%esp 将下一个待执行进程next的系统空间堆栈指针切换进来.由于硬件相关的进程上下文信息都跟内核堆栈相关,所以第二步和第三步可以说完成了进程切换的大部分工作.(从21行开始就在使用进程next的堆栈了。换言之,从21开始,“当前进程”已经是next而不是prev了)
    • 第四步,movl $1f, %1(prev->thread.eip), 将25行的地址保存在prev->thread.eip中
    • 第五步,pushl %6(next->thread.eip), jmp __switch_to 这是整个宏的玄机所在,首先将next->thread.eip压栈, 然后跳转到__switch_to()函数中去执行,这里要注意的是,C函数执行完成后,其返回地址在栈顶位置,又由于现在栈顶的值是next->thread.eip, 这样,next->thread.eip(即上一次它被调离时25行的地址)即为这个函数的返回地址,这样,函数返回后,将跳转到25行这个地址去执行.
    • 第六步,popl %%ebp, popfl 恢复新进程的相关寄存器的值.由于在第三步中,进程栈切换已经完成,所以,这里是对新的进程栈的恢复操作.

    注:
    在内核代码中当需要访问当前进程的task_struct结构时使用的指针current实际上是个宏定义,它是根据当前进程的堆栈指针ESP计算出来的
    • eip是下条要执行的指令地址
    • ebp指向内核堆栈基地址
    • esp指向内核堆栈栈顶


    第一步
    pushfl
    pushl %%ebp
                       进程prev的内核堆栈

                       |           |
                       |           |
                       |-----------|
                       |  eflags   |
                       |  ebp      |
               %%esp-->|           |

    第二步
    movl %%esp,%0  //prev->thraad.esp(%0)
                       进程prev的内核堆栈
                       |           |
                       |           |
                       |-----------|
                       |  eflags   |
            (%%esp)    |  ebp      |
    prev->thread.esp-->|           |


    第三步
    movl %5,%%esp   //next->thread.esp(%5)

                    进程next的内核堆栈

                    |           |
                    |           |
                    |           |
                    | eflags    |
                    | ebp       |
            %%esp-->|           |
     (next->thread.esp)


    第四步
    movl $1f,%1     //prev->thread.eip(%1)

    prev->thread.eip = (第25行的地址)
    作为进程prev下一次被调度运行时的“返回地址”


    第五步
    pushl %6        //next->thread.eip(%6)

                 进程next的内核堆栈
               |                |
               |                |
               |                |
               |eflags          |
               |ebp             |
               |next->thread.eip|
       %%esp-->|                |
    将next->thread.eip压入堆栈(next的内核堆栈?):这里的next->thread.eip正是
    进程next上一次被调离时在第21行中保存的。它也执行这里的标号"1", 即25行的popl指令

      本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
      转藏 分享 献花(0

      0条评论

      发表

      请遵守用户 评论公约

      类似文章 更多