在x86-64下切换堆栈的最小代码是什么?我尝试在Windows和Linux下实现光纤,没有getcontext或setjmp +内联汇编。它真的像交换$ rsp和$ rbp一样简单吗?因为我可以轻松地做到这一点。我只是不确定如何去做。我的x86-64知识生锈了。
答案 0 :(得分:2)
将RSP更改为指向不同的堆栈必须作为上下文切换的一部分来完成,该上下文切换将保存旧线程/光纤中的所有寄存器并从新寄存器加载保存的体系结构状态。不只是RBP,而是所有RAX-RDI和R8-R15,以及RIP(通过jmp
或ret
)。我认为所有其他调用保留的架构状态,包括Windows x86-64上的xmm6-15。如果您的代码更改了MXCSR或x87控制寄存器,则还需要保存/恢复它们。
但是如果你将你的上下文切换放在noinline
函数中,编译器将为常规函数调用(有效返回的时间很晚)和函数调用生成代码已经破坏了所有的召唤破坏寄存器。您不必保存来电者的zmm0-31或MPX bnd
寄存器或RFLAGS。因此,使用xsaveopt
/ xrstor
保存FPU / SIMD状态可能不值得。
如果你保留其他regs未修改,你将有一个不好的时间,因为切换到一个新的堆栈和旧的寄存器的新代码基本上是与编译器下的反复调用保留寄存器相同的事情,即违反ABI
您不需要保存RFLAGS
,因为用户空间中唯一可以更改的内容是条件代码,而这些代码是被调用的。 ABI /调用约定已经要求在函数调用/返回时清除DF。
答案 1 :(得分:1)
上下文切换由ABI确定(必须保留被调用者保存的寄存器)。 boost.context已经为几种架构提供了实现。 boost.fiber是基于boost.context的带有std :: thread-like API的光纤的抽象。
答案 2 :(得分:0)
Microsoft确实提供了setjmp,setjmp3和longjmp。他们还提供Using setjmp/longjmp。
下面的代码示例可以在Explain setjmp() and longjmp() Functions in Standard C Library with Examples
找到/* setjmp_longjmp.c -- program handles error through 'setjmp()' */
/* and longjmp() */
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
/* declare variable of type jmp_buf */
jmp_buf resume_here;
void hello(void);
int main(void)
{
int ret_val;
/* Initialize 'resume_here' by calling setjmp() */
if (setjmp(resume_here)) {
printf("After \'longjump()\', back in \'main()\'\n");
printf("\'jump buffer variable \'resume_here\'\' becomes "
"INVALID!\n");
}
else {
printf("\'setjmp()\' returns first time\n");
hello();
}
return 0;
}
void hello(void)
{
printf("Hey, I'm in \'hello()\'\n");
longjmp(resume_here, 1);
/* other code */
printf("can't be reached here because I did longjmp!\n");
}