分享

switch

 Liucw2012 2012-04-27

switch_to中的汇编

March 26th, 2009 by 王 聪 Leave a reply ?

switch_to()的代码在arch/x86/include/asm/system.h中,如下:

C:
  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.              "jmp __switch_to\n"    /* regparm call  */ \
  19.              "1:\t"                     \
  20.              "popl %%ebp\n\t"       /* restore EBP   */ \
  21.              "popfl\n"          /* restore flags */ \
  22.                                     \
  23.              /* output parameters */                \
  24.              : [prev_sp] "=m" (prev->thread.sp),        \
  25.                [prev_ip] "=m" (prev->thread.ip),        \
  26.                "=a" (last),                 \
  27.                                     \
  28.                /* clobbered output registers: */        \
  29.                "=b" (ebx), "=c" (ecx), "=d" (edx),      \
  30.                "=S" (esi), "=D" (edi)               \
  31.                                         \
  32.                /* input parameters: */              \
  33.              : [next_sp]  "m" (next->thread.sp),        \
  34.                [next_ip]  "m" (next->thread.ip),        \
  35.                                         \
  36.                /* regparm parameters for __switch_to(): */  \
  37.                [prev]     "a" (prev),               \
  38.                [next]     "d" (next)                \
  39.                                     \
  40.              : /* reloaded segment registers */         \
  41.             "memory");                  \
  42. } while (0)

根据ABI约定和内联汇编,ebx, ecx, edx, esi, edi这几个寄存器是由编译器自动保存和恢复的。这一点可能不太好理解,举个例子,看下面的代码中的ecx:

C:
  1. #include <stdio.h>
  2.  
  3. void modify_ecx(void) {
  4.   unsigned long ecx;
  5.  
  6.  __asm__ (
  7.   "movl $1, %%ecx\n\t"
  8.  :"=c"(ecx)
  9.  :
  10.  );
  11. }
  12.  
  13. void test(void) {
  14.  unsigned long ecx;
  15.  
  16.  __asm__ volatile(
  17.  "nop\n\t"
  18.  "call modify_ecx\n\t"
  19.  : "=c" (ecx)
  20.  :
  21.  : "memory"
  22.  );
  23.  printf("ecx=%ld\n", ecx);
  24. }
  25.  
  26. int main(void) {
  27.  test();
  28.  return 0;
  29. }

这里的test()就相当于内核源代码中“调用”switch_to()的context_switch(),我们来查看其对应的汇编代码(注意要加-O0):

main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ecx
        subl    $8, %esp
        call    test
        movl    $0, %eax
        addl    $8, %esp
        popl    %ecx
        leal    -4(%ecx), %esp
        ret

可见,在调用test()之前,编译器已经自动完成了保存ecx的操作,而调用之后它又会恢复ecx的值。

而ebp,eflags是手工压入堆栈,并在switch回来后恢复出来的。esp和eip保存在相应的task_struct结构体里。

需要额外说明的是那个jmp,因为这里的参数传递是通过寄存器完成的,具体说是用了eax和edx这个两个寄存器,所以再jmp其实就和call一样了,不过call是要把ebp入栈的,而jmp不需要,这里也不需要。

另外一个可能的问题是:为什么switch_to()有三个参数?我们切换的进程明明是两个啊!这里问题的所在是进程切换时堆栈的切换,如果不使用三个参数,切换的堆栈中仍然保存的是切换前的参数,再切换回来时prev很可能不对了,所以需要一个参数来修正,这个参数又正好是__switch_to的返回值。这样问题就解决了。

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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多