Chinaunix首页 | 论坛 | 博客
  • 博客访问: 51633
  • 博文数量: 75
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2013-04-12 12:32
文章分类

全部博文(75)

文章存档

2013年(75)

我的朋友

分类: LINUX

2013-04-12 13:02:51

内敛汇编版本的switch_to

#define switch_to(prev, next, last)     \
do {         \
 /*        \
  * Context-switching clobbers(彻底击败) all registers, so we clobber \
  * them explicitly, via unused output variables.  \
  * (EAX and EBP is not listed because EBP is saved/restored \
  * explicitly for wchan access and EAX is the return value of \
  * __switch_to())      \
  */        \
 unsigned long ebx, ecx, edx, esi, edi;    \
         \
 asm volatile("pushfl\n\t"  /* save    flags */ \
       "pushl %%ebp\n\t"  /* save    EBP   */ \
       "movl %%esp,%[prev_sp]\n\t" /* save    ESP   */ \
       "movl %[next_sp],%%esp\n\t" /* restore ESP   */ \
       "movl $1f,%[prev_ip]\n\t" /* save    EIP   */ \
       "pushl %[next_ip]\n\t" /* restore EIP   */ \
       "jmp __switch_to\n" /* regparm call  */ \
       "1:\t"      \
       "popl %%ebp\n\t"  /* restore EBP   */ \
       "popfl\n"   /* restore flags */ \
         \
       /* output parameters */                       \
       : [prev_sp] "=m" (prev->thread.sp),  \
       /*m表示把变量放入内存,即把[prev_sp]存储的变量放入内存,最后再写入prev->thread.sp*/\
         [prev_ip] "=m" (prev->thread.ip),  \
         "=a" (last),                                           \
         /*=表示输出,a表示把变量last放入ax,eax = last*/         \
         \
         /* clobbered output registers: */  \
         "=b" (ebx), "=c" (ecx), "=d" (edx),  \
         /*b 变量放入ebx,c表示放入ecx,d放入edx,S放入si,D放入edi*/\
         "=S" (esi), "=D" (edi)    \
                \
         /* input parameters: */    \
       : [next_sp]  "m" (next->thread.sp),  \
       /*next->thread.sp 放入内存中的[next_sp]*/\
         [next_ip]  "m" (next->thread.ip),  \
                \
         /* regparm parameters for __switch_to(): */ \
         [prev]     "a" (prev),    \
         /*eax = prev  edx = next*/\
         [next]     "d" (next)    \
         \
       : /* reloaded segment registers */   \
   "memory");     \
} while (0)

 

 

标准汇编版本的switch_to:

1 把prev和next分别保存在寄存器中,即寄存器传参

movl prev,%eax

movl next,%edx

 

2 把eflags和ebp保存在当前的堆栈中

pushfl

pushl %ebp

 

3 把esp的内容保存到prev->thread.esp中,以使该字段指向prev内核栈的栈顶

movl %esp,484(%eax)

注:484(%eax) ,表示内存但愿的地址=(%eax) + 484

 

4 把next->thread.sp装入esp,内核开始在next的指令空间中操作,这条指令完成了进程之间的切换。

可以会想thread_info数据结构,内核栈和进程描述符组成的8K的数据结构

movl 484(%edx),%esp

 

5 把标记为1f的地址存入prev->thread.eip,即被替换出的进程在下次被schedule()选择执行时,从这条指令开始执行

movl $1f,480(%eax)

 

6 把next->thread.eip(绝大多数情况是一个被标记为1的地址)的值压入next的内核栈

pushl 480(%edx)

 

7 跳到__swtich_to() c语言函数开始执行

jmp __swtich_to

 

8 这里被进程next替换的进程prev再次获得CPU:它执行一些保存eflags和ebp的寄存器内容指令,这两条指令的第一条指令被标记为1(这是《深入理解unix》书上说的)。感觉从__swtich_to返回时ip已经指向了next的第一条指令,因为在执行ret指令的时候,把sp处保存的next->ip弹出赋给了next的eip,所以书上说prev再次获得cpu应该是不对的。

1:

    pop %ebp

    popfl

 

从标号1:开始已经进入了next的指令空间,可以这么理解1:就是next进程的第一条指令,执行

 

 

    pop %ebp

    popfl

后完成了最后的切换,即栈和状态字的恢复,恢复成next的堆栈基址和状态字。

 

个人理解:linux里面的每个进程(新创建的进程除外)的第一条指令应该都是

1:

    pop %ebp

    popfl

每个进程一开始全是执行这两条指令,之后才各自干自己的事去

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