前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C协程】ucontext入解

【C协程】ucontext入解

作者头像
王亚昌
发布2018-08-03 15:48:21
1.7K0
发布2018-08-03 15:48:21
举报
文章被收录于专栏:王亚昌的专栏王亚昌的专栏

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时则直接退出。

代码语言:javascript
复制
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;

二、认识函数族

代码语言:javascript
复制
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中

代码语言:javascript
复制
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创建

代码语言:javascript
复制
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时,线程退出。

代码语言:javascript
复制
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手册示例

代码语言:javascript
复制
#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);
}

输出如下:

代码语言:javascript
复制
func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: started
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: returning
func1: returning
main: exiting

3.2 生产者消费者

代码语言:javascript
复制
#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;
}

输出如下:

代码语言:javascript
复制
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
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年11月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档