源码如下(使用VS继承的clang编译器):
#include <stdint.h>
#include <stdio.h>
#include <setjmp.h>
#if defined(_WIN64)
static __inline__ __attribute__((__always_inline__))
void task_set_stack(uintptr_t stack)
{
__asm__("movq %0, %%rsp" : : "r"(stack));
}
#elif defined(_WIN32)
static __inline__ __attribute__((__always_inline__))
void task_set_stack(uintptr_t stack)
{
__asm__("movl %0, %%esp" : : "r"(stack));
}
#else
# error "not supported"
#endif
struct task_t {
jmp_buf *ret; // for return to scheduler
jmp_buf *pos; // for go back from scheduler
uint32_t stack_size;
uint64_t stack[32 * 1024] __attribute__((__aligned__(16)));
};
static void __task_entry(struct task_t *task)
{
printf("task is running...\n");
longjmp(*(task->ret), -1);
}
struct task_t task = {
.stack_size = sizeof(task.stack),
};
int main()
{
jmp_buf ret;
task.ret = &ret;
if (!setjmp(ret)) {
task_set_stack((uintptr_t)(&task.stack[task.stack_size >> 3]));
__task_entry(&task);
} else {
printf("back from task...\n");
}
return 0;
}
在arm/win_x86/linux_x86/linux_x64下都可以使用,以上代码只是演示问题,并非协程的完整代码。
以下说明也只是上述代码的说明,并非实际协程的实现:
在setjmp之后,会设置堆栈到具体协程的堆栈,然后调用协程入口;协程返回的时候,调用longjmp调回到原来主函数的位置,打印信息后退出。这里longjmp是会做堆栈SP寄存器的切换的,x86下没问题,x64下会引发异常,说是不正确或者无效配置的堆栈。但是实际堆栈已经按照x64的要求,是16字节对其的。
x64下做堆栈切换,除了设置RSP寄存器外,还有其他需要处理的吗?