Chinaunix首页 | 论坛 | 博客
  • 博客访问: 572582
  • 博文数量: 168
  • 博客积分: 62
  • 博客等级: 民兵
  • 技术积分: 442
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-30 11:45
文章分类

全部博文(168)

文章存档

2016年(2)

2015年(19)

2014年(98)

2013年(22)

2012年(6)

2011年(21)

分类:

2011-10-17 17:30:52

原文地址:switch_to 宏 作者:chmxu

switch_to宏是平台相关的,用来切换进程,它在调度策略决定切换的情况下被调用,当宏完成时,执行流已经到了另外一个进程了

代码:


#define switch_to(prev,next,last) do { \
        unsigned long esi,edi; \
        asm volatile("pushfl\n\t" \
                     "pushl %%ebp\n\t" \
                     "movl %%esp,%0\n\t" /* save ESP */ \
                     "movl %5,%%esp\n\t" /* restore ESP */ \
                     "movl $1f,%1\n\t" /* save EIP */ \
                     "pushl %6\n\t" /* restore EIP */ \
                     "jmp __switch_to\n" \
                     "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)



首先, 无论什么进程执行进程切换,代码都是这段,每条指令都相同, 唯一不同的就是参数不同,数据不同,我很晚才想明白这个
这个完成栈的切换,说白了,进程切换本质上就是栈的切换和下一条指令位置的更改

首先保存eflag
然后
"pushl %%ebp\n\t"
"movl %%esp,%0\n\t" /* save ESP */

这两条指令显示的保存%ebp到栈上,%ebp 属于local register, 也就是说函数的调用者在调用完一个函数后不能保证函数返回之后寄存器的值不会被改变,同样的还有esi, edi, ebx,前两个用于字符串指令,ebx用于保存全局变量的值, 如果函数需要使用更多的寄存器,编译器/函数需要首先保存,然后返回之前恢复到调用者的初始值

这也就顺便把
"=S" (esi),"=D" (edi) 说了。其实这仅仅提示编译器保存它们,然后恢复。 查看这段代码就会发现,编译出来的代码有pushl %esi; pushl %edi; 然后后面pop

接下来

 "movl %%esp,%0\n\t" /* save ESP */ \
"movl %5,%%esp\n\t" /* restore ESP */ \

用来保存当前进程的栈位置%esp 到进程描述符中,以便下次作为next的时候被恢复(即上面的后面那条指令)
到这里栈的值已经改变到新的值了(需要注意的事进程切换只发生在内核态,这%esp是内核进程的内核栈,永远都是,因为用户态执行的进程不会导致切换,只有被中断后才有可能)

 "movl $1f,%1\n\t" /* save EIP */ \
 "pushl %6\n\t" /* restore EIP */ \

前面这条保存EIP不用说,后面这条把下个进程的EIP恢复,而且jmp 到__switch_to函数,这样,在__switch_to返回的时候再ret(函数最后执行一条指令)的时候,就返回到next进程以前被替换的位置了,进程next接着上次被调度出去的那里继续执行

popl %ebp也就恢复了next的%ebp了

所以switch_to比起别的宏/函数调用对编译器并没有什么特殊的,编译器不知道进程切换,它只知道指令。 它不知道这个调用会导致别的进程执行。

不过这个宏第一次看还真有点难理解,特别是对linux进程的实现一点都不了解的话




阅读(874) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~