x86-64下的快速堆栈切换

问题描述 投票:3回答:3

在x86-64下切换堆栈的最小代码是什么?我正在尝试在Windows和Linux下实现光纤,没有getcontext或setjmp +内联汇编。它真的像交换$ rsp和$ rbp一样简单吗?因为我可以轻松地做到这一点。我只是不确定如何去做。我的x86-64知识生锈了。

c linux windows assembly
3个回答
2
投票

将RSP更改为指向不同的堆栈必须作为上下文切换的一部分来完成,该切换保存来自旧线程/光纤的所有寄存器并从新寄存器加载保存的架构状态。不只是RBP,而是所有RAX-RDI和R8-R15,以及RIP(通过jmpret)。我认为所有其他调用保留的架构状态,包括Windows x86-64上的xmm6-15。如果您的代码更改了MXCSR或x87控制寄存器,则还需要保存/恢复它们。

但是如果你把你的context-switch放在一个noinline函数中,编译器会为常规函数调用生成代码(它会在很晚的时间内有效地返回),而函数调用已经破坏了所有的call-clobbered寄存器。您不必保存调用者的zmm0-31或MPX bnd寄存器或RFLAGS。因此,使用xsaveopt / xrstor保存FPU / SIMD状态可能不值得。

如果你保留其他寄存器未经修改,那么你将有一个不好的时间,因为切换到一个新的堆栈和带有旧寄存器的新代码基本上与从编译器中删除调用保留的寄存器相同,即违反了ABI。

你不需要保存RFLAGS,因为在用户空间中唯一可以改变的是条件代码,而那些是call-clobbered。 ABI /调用约定已经要求DF在函数调用/返回时是清除的。


1
投票

上下文切换由ABI确定(必须保留被调用者保存的寄存器)。 boost.context已经为几种架构提供了实现。 boost.fiber是一个基于boost.context的带有std :: thread-like API的光纤抽象。


0
投票

微软确实提供setjmpsetjmp3longjmp。他们还提供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");
}
© www.soinside.com 2019 - 2024. All rights reserved.