Linux 64位上下文切换

在32位模式的switch_tomacros中,在调用__switch_to函数之前执行以下代码:

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 */ \ __switch_canary \ "jmp __switch_to\n" /* regparm call */ 

EIP被推入堆栈(恢复EIP)。 当__switch_完成时,有一个ret返回到那个位置。 这里是对应的64位代码:

  asm volatile(SAVE_CONTEXT \ "movq %%rsp,%P[threadrsp](%[prev])\n\t" /* save RSP */ \ "movq %P[threadrsp](%[next]),%%rsp\n\t" /* restore RSP */ \ "call __switch_to\n\t" 

在那里,只有rsp被保存和恢复。 我认为RIP已经在栈顶了。 但是我不能在那里完成指令。 64位上下文是如何切换的,尤其是对于RIP寄存器,实际上是如何切换的?

提前致谢!

在32位内核中, thread.ip可能是以下之一:

  • switch_to1标签
  • ret_from_fork
  • ret_from_kernel_thread

通过使用push + jmp对模拟call来确保返回到适当的位置。

在64位内核中, thread.ip不是这样使用的。 在call (在32位的情况下曾经是1标签),执行总是继续。 因此,不需要模拟call ,可以正常完成。 在__switch_to返回之后,使用条件跳转调度到ret_from_fork (您已省略此部分):

 #define switch_to(prev, next, last) \ asm volatile(SAVE_CONTEXT \ "movq %%rsp,%P[threadrsp](%[prev])\n\t" /* save RSP */ \ "movq %P[threadrsp](%[next]),%%rsp\n\t" /* restore RSP */ \ "call __switch_to\n\t" \ "movq "__percpu_arg([current_task])",%%rsi\n\t" \ __switch_canary \ "movq %P[thread_info](%%rsi),%%r8\n\t" \ "movq %%rax,%%rdi\n\t" \ "testl %[_tif_fork],%P[ti_flags](%%r8)\n\t" \ "jnz ret_from_fork\n\t" \ RESTORE_CONTEXT \ 

ret_from_kernel_thread被合并到ret_from_fork路径中,在entry_64.S使用另一个条件跳转:

 ENTRY(ret_from_fork) DEFAULT_FRAME LOCK ; btr $TIF_FORK,TI_flags(%r8) pushq_cfi $0x0002 popfq_cfi # reset kernel eflags call schedule_tail # rdi: 'prev' task parameter GET_THREAD_INFO(%rcx) RESTORE_REST testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread? jz 1f