c++ uconcontext.h实现协程

什么是协程?

协程是一种程序组件,是由子例程(过程、函数、例程、方法、子程序)的概念泛化而来的,子例程只有一个入口点且只返回一次,而协程允许多个入口点,可以在指定位置挂起和恢复执行。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。

一个线程可以拥有多个协程,但是一个时刻只能有协程在执行,协程的调度是非强占式的,只有协程自己主动让出执行权才切换。而非像线程一样是由操作系统调度。

实现协程的重点就是保存当前上下文,切换到要执行的上下文,结束后再返回到保存的上下文

ucontext.h库

ucontext.h库中定义了一个ucontext_t结构体

typedef struct ucontext_t
  {
    unsigned long int __ctx(uc_flags);
    struct ucontext_t *uc_link; //后继上下文
    stack_t uc_stack;           //该上下文中使用的栈
    mcontext_t uc_mcontext;     
    sigset_t uc_sigmask;
    struct _libc_fpstate __fpregs_mem;
  } ucontext_t;

这个结构体是用来保存上下文的

并且定义了4个函数用于操作结构体

int getcontext(ucontext_t * ucp);
获取当前上下文, 初始化ucp结构体, 将当前上下文保存到ucp中
void makecontext(ucontext_t *ucp, void(*func)(), int argc, ...);
创建一个上下文
int setcontext(const ucontext_t *ucp);
设置当前的上下文为ucp
int swapcontext(ucontext_t *oucp, ucontext_t *ucp);
保存当前上下文至oucp, 激活ucp上下文

库的使用示例

#include<stdio.h>
#include<ucontext.h>

void funtion(){
    printf("run this\n");
}

int main()
{
    printf("in main\n");
    char stack[1024];
    ucontext_t main,other;
    getcontext(&main);   //获取当前上下文
    
    main.uc_stack.ss_sp = stack;          //指定栈空间
    main.uc_stack.ss_size = sizeof(stack);//指定栈空间大小
    main.uc_stack.ss_flags = 0;
    main.uc_link = &other;                //将后继上下文指向other

    makecontext(&main,funtion,0);         //为main指定要执行的函数
    
    swapcontext(&other,&main);            //激活main,并将当前上下文保存到other
    printf("in main\n");
    return 0;
}

output

in main
run this
in main

核心代码

ucontext_t main,other;
getcontext(&main);                    //获取当前上下文
main.uc_stack.ss_sp = stack;          //指定栈空间
main.uc_stack.ss_size = sizeof(stack);//指定栈空间大小
main.uc_stack.ss_flags = 0;
main.uc_link = &other;                //将后继上下文指向other
makecontext(&main,funtion,0);         //为main指定要执行的函数
swapcontext(&other,&main);            //激活main,并将当前上下文保存到other

代码地址

https://github.com/linzhongli/coroutine