• ucontext 上下文


    头文件 中共定义了四个函数:
    ucontext结构体定义:

    typedef struct ucontext
      {
        unsigned long int uc_flags;
        struct ucontext *uc_link;//后序上下文
        __sigset_t uc_sigmask;// 信号屏蔽字掩码
        stack_t uc_stack;// 上下文所使用的栈
        mcontext_t uc_mcontext;// 保存的上下文的寄存器信息
        long int uc_filler[5];
      } ucontext_t;
    
    //其中mcontext_t 定义如下
    typedef struct
      {
        gregset_t __ctx(gregs);//所装载寄存器
        fpregset_t __ctx(fpregs);//寄存器的类型
    } mcontext_t;
    
    //其中gregset_t 定义如下
    typedef greg_t gregset_t[NGREG];//包括了所有的寄存器的信息
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    getcontext(ucontext_t *ucp):保存当前CPU上下文信息到指定ucontext_t 结构体;

    setcontext(const ucontext_t *ucp):将上下文ucp恢复到CPU

    makecontext(ucontext_t *ucp, void (*func)(), int argc, …):func是上下文的入口函数;argc是入口函数的参数个数,后面的…是具体的入口函数参数,该参数必须为整形值。这里就是将func的地址保存到寄存器中,把ucp上下文结构体下一条要执行的指令rip改变为func函数的地址。并且将其所运行的栈改为用户自定义的栈

    swapcontext(ucontext_t *oucp, ucontext_t *ucp):将当前cpu中的上下文信息保存带oucp结构体变量中,然后将ucp中的结构体的上下文信息恢复到cpu中,这里可以理解为调用了两个函数,第一次是调用了getcontext(oucp)然后再调用setcontext(ucp)

    #include 
    
    #define _XOPEN_SOURCE
    #include 
    #undef _XOPEN_SOURCE
    
    int fib_res;
    ucontext_t main_ctx, fib_ctx;
    
    char fib_stack[1024 * 32];
    
    void fib() {
        // (1)
        int a0 = 0;
        int a1 = 1;
    
        while (1) {
            fib_res = a0 + a1;
            a0 = a1;
            a1 = fib_res;
    
            // send the result to outer env and hand over the right of control.
            swapcontext(&fib_ctx, &main_ctx);  // (b)
            // (3)
        }
    }
    
    int main(int argc, char **argv) {
        // initialize fib_ctx with current context.
        getcontext(&fib_ctx);
        fib_ctx.uc_link = 0;  // after fib() returns we exit the thread.
        fib_ctx.uc_stack.ss_sp = fib_stack;  // specific the stack for fib().
        fib_ctx.uc_stack.ss_size = sizeof(fib_stack);
        fib_ctx.uc_stack.ss_flags = 0;
        makecontext(&fib_ctx, fib, 0);  // modify fib_ctx to run fib() without arguments.
    
        while (1) {
            // pass the right of control to fib() by swap the context.
            swapcontext(&main_ctx, &fib_ctx);  // (a)
            // (2)
            printf("%d\n", fib_res);
            if (fib_res > 100) {
                break;
            }
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    代码中相关的调用我都加了注释,下面我来简单描述一下执行的过程:

    首先标号 (a) 的调用会将控制权转交给 fib 函数同时保存状态,由于 fib_ctx 之前是由 makecontext 函数修改过的,所以这次跳转会跳转到标号 (1) 这个位置。
    fib 计算好一次迭代的结果,通过标号 (b) 将控制权交回 main 函数,跳转到之前保存的状态,即标号 (2)。
    如果 fib_res <= 100,标号 (a) 继续执行,由于标号 (b) 的调用保存了之前 fib 的状态,所以这次跳转到标号 (3) 的位置,fib 继续之前的状态执行。
    至此,我们就在 C 中实现了这个简单的协程。

    帮助理解:
    下图是协程切换前的状态:
    在这里插入图片描述
    从协程 1 切换到协程 2 后的状态如下图所示:
    在这里插入图片描述

  • 相关阅读:
    细说GaussDB(DWS)复杂多样的资源负载管理手段
    51单片机 串口通信
    基于springboot+vue实现MOBA类游戏攻略平台项目【项目源码+论文说明】
    three.js学习笔记
    ClickBench 最新跑分排行榜,黑马出现!
    Bitxhub跨链平台
    通关 MySQL获奖名单已公布
    全国批发市场情况萧条,进销存系统或是业务转机
    设计模式-02-工厂模式
    nuc10黑苹果无法wifi上网
  • 原文地址:https://blog.csdn.net/qq_46495964/article/details/126397094