0%

C 实现用户态上下文切换

在 linux 下,glibc 提供了几个函数来给用户实现用户态的上下文切换:

  • getcontext:初始化一个上下文参数
  • setcontext:根据参数还原上下文
  • makecontext:创建一个新的上下文
  • swapcontext:上下文切换

getcontext 和 setcontext

详细文档:https://linux.die.net/man/3/getcontext

1
2
3
4
#include <ucontext.h>

int getcontext(ucontext_t *ucp);
int setcontext(const ucontext_t *ucp);
  • getcontext: 保存当前上下文到第一个参数
  • setcontext: 设置当前上下文为第一个参数(成功调用的时候不会返回)。
    • 如果第一个参数是通过 getcontext 获取的,则调用之后继续往下执行,
    • 如果是通过 makecontext 创建的,程序会调用 makecontext 的第二个参数指向的函数,
    • 当函数执行完毕,转到 uc_link 指向的上下文,如果 uc_linknull,则线程退出。

makecontext

详细文档:https://man7.org/linux/man-pages/man3/swapcontext.3.html

1
2
3
4
5
#include <ucontext.h>

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
int swapcontext(ucontext_t *restrict oucp,
const ucontext_t *restrict ucp);
  • makecontext: 修改第一个参数为 getcontext 之后的 ucontext_t,如 getcontext(&uctx); makecontext(&uctx);
  • swapcontext: 保存当前上下文到第一个参数,然后切换到第二个参数对应的上下文

ucontext_t.uc_link 在当前上下文结束后的上下文

getcontext 和 setcontext 成功的时候返回 0,失败的时候返回 -1。

注意事项

  • OSX 下 ucontext_t 的栈大小要大于等于 32Kb,否则 makecontext 调用无效。
  • context 对应的函数里面需要用到栈的时候,是线程共享的栈,而不是 ucontext_t.uc_stack 指定的栈。所以给 ucontext_t.uc_stack 设置初始化的栈的时候,不用考虑 context 对应函数需要占用多少栈空间。