【C协程】ucontext入解

In  a  System V-like environment, one has the type ucontext_t defined in <ucontext.h> and the four functions get-context(2), setcontext(2), makecontext() and swapcontext() that allow user-level context switching between multi-ple threads of control within a process.

在系统V环境中,我们可以通过getcontext,setcontext,makecontext,swapcontext函数族,在单进程中实现用户态上下文的切换。


一、认识ucontext

首先我们来认识一下这个结构中的文键变量,结构如下,需要关注到的是uc_stack,以及uc_link,uc_stack指向上下文切换需要保存的栈空间,需要用户分配。uc_link指向当时上下文执行函数成功退出后,指定激活的上下文,如果为uc_link为NULL时则直接退出。

typedef struct ucontext
{
    unsigned long int uc_flags;
    struct ucontext *uc_link;
    stack_t uc_stack;
    mcontext_t uc_mcontext;
    __sigset_t uc_sigmask;
    struct _fpstate __fpregs_mem;
} ucontext_t;


二、认识函数族

int getcontext(ucontext_t *ucp);
getcontext(2) gets the current context of the calling process, storing it in the ucontext struct  pointed  to  by ucp.

getcontext把当前的上下文保存在ucp中


int setcontext(const ucontext_t *ucp);
setcontext(2)  sets  the  context of the calling process to the state stored in the ucontext struct pointed to by ucp. The struct must either have been created by getcontext(2) or have been passed as the third parameter of  the sigaction(2) signal handler.

setcontext把当前进程恢复到ucp指针指向的上下文,ucp必须由getcontext创建


void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);

The  makecontext()  function  modifies  the  context pointed to by ucp (which was obtained from a call to getcon-text(2)).  Before invoking makecontext(), the caller must allocate a new stack for this context  and  assign  its address to ucp->uc_stack, and define a successor context and assign its address to ucp->uc_link.

When  this  context  is  later  activated (using setcontext(2) or swapcontext()) the function func is called, and passed the series of integer (int) arguments that follow argc; the caller must specify the number of these  argu-ments in argc.  When this function returns, the successor context is activated.  If the successor context pointer is NULL, the thread exits.

makecontext修改ucp指针指向的上下文,在调用makecontext之前,必须为ucp对象分配一个新栈并赋为ucp->uc_stack,同时定义一个成功返回后恢复的上下文并把地址赋给ucp->uc_link。当ucp被激活后(通过setcontext或swapcontext激活),func函数会被调用,并把一系统参数传凝视给func函数。当func函数返回原来,uc_link会被激活,uc_link为NULL时,线程退出。


int swapcontext(ucontext_t *oucp, ucontext_t *ucp);
The swapcontext() function saves the current context in the structure pointed to by oucp, and then activates  the context pointed to by ucp.

swapcontext把当前上下文保存在oucp中,并激活ucp。


三、示例

3.1 MAN手册示例

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

static ucontext_t uctx_main, uctx_func1, uctx_func2;

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

    static void
func1(void)
{
    printf("func1: started\n");
    printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n");
    if (swapcontext(&uctx_func1, &uctx_func2) == -1)
        handle_error("swapcontext");
    printf("func1: returning\n");
}

    static void
func2(void)
{
    printf("func2: started\n");
    printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n");
    if (swapcontext(&uctx_func2, &uctx_func1) == -1)
        handle_error("swapcontext");
    printf("func2: returning\n");
}

    int
main(int argc, char *argv[])
{
    char func1_stack[16384];
    char func2_stack[16384];

    if (getcontext(&uctx_func1) == -1)
        handle_error("getcontext");
    uctx_func1.uc_stack.ss_sp = func1_stack;
    uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
    //uctx_func1.uc_link = &uctx_func2;
    uctx_func1.uc_link = &uctx_main;
    makecontext(&uctx_func1, func1, 0);

    if (getcontext(&uctx_func2) == -1)
        handle_error("getcontext");
    uctx_func2.uc_stack.ss_sp = func2_stack;
    uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
    /*  Successor context is f1(), unless argc > 1 */
    uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1;
    makecontext(&uctx_func2, func2, 0);

    printf("main: swapcontext(&uctx_main, &uctx_func2)\n");
    if (swapcontext(&uctx_main, &uctx_func2) == -1)
        handle_error("swapcontext");

    printf("main: exiting\n");
    exit(EXIT_SUCCESS);
}
输出如下 :

func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: started
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: returning
func1: returning
main: exiting


3.2 生产者消费者

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

static ucontext_t uc_loop, uc_main, uc_consume, uc_produce;
static int ci, pi, loop;

static void cb_consume()
{
    printf("consume %d\n", ci);
    ci++;
    sleep(1);
}

static void cb_produce()
{
    printf("produce %d\n", pi);
    pi++;
}

int main(void)
{
    char  consume_stack[1024], produce_stack[1024];

    getcontext(&uc_loop);

    getcontext(&uc_produce);
    uc_produce.uc_stack.ss_sp = produce_stack;
    uc_produce.uc_stack.ss_size = sizeof(produce_stack);
    uc_produce.uc_link = &uc_consume;
    makecontext(&uc_produce, cb_produce, 0);
    printf("finish make produce\n");

    getcontext(&uc_consume);
    uc_consume.uc_stack.ss_sp = consume_stack;
    uc_consume.uc_stack.ss_size = sizeof(consume_stack);
    uc_consume.uc_link = &uc_main;
    makecontext(&uc_consume, cb_consume, 0);
    printf("finish make consume\n");

    printf("swap main to produce\n");
    swapcontext(&uc_main, &uc_produce);
    printf("swap back to main\n");

    sleep(1);
    loop++;
    if (loop < 2) setcontext(&uc_loop);

    return 0;
}

输出如下:

finish make produce
finish make consume
swap main to produce
produce 0
consume 0
swap back to main
finish make produce
finish make consume
swap main to produce
produce 1
consume 1
swap back to main



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值