您现在的位置是: 网站首页 > 程序设计  > Linux 

协成实现之ucontext簇函数

2022年4月5日 21:54 2339人围观

简介在协程的时候需要在用户程序中实现调度切换,多数人认为要实现切换可能需要嵌入汇编实现,其实不然。在Linux中还有两组系统调用可以实现切换,那就是是setjmp/longjmp和ucontext簇函数

ucontext_t定义

在5.4的内核中,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; 

在所有字段中,我们只关心uc_linkuc_stack两个参数。

uc_link指的是当前上下文结束后需要执行的上下文,如果设为NULL,则表示当前结束后进程退出。

uc_stack中需要指定协成的栈地址和大小。

getcontext

int getcontext(ucontext_t *ucp);

该函数用来获取当前上下文保存到ucp结构中

makecontext

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

该函数用来修改上下文信息,可以用来设置上下文的入口函数,其中ucp必须先经过getcontext初始化。

setcontext

int setcontext(const ucontext_t *ucp)

该函数是将上下文恢复到CPU中并执行,ucp上下文是由getconextmakecontext设置而来。

swapcontext

int swapcontext(ucontext_t *oucp, ucontext_t *ucp)

该函数用来切换上下文,同时保存当前上下问到oucp,ucp上下文是由getconextmakecontext设置而来。

例1、用getcontext/setcontext实现for循环

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

int main() { 
    int idx = 0; 
    ucontext_t ctx1; 
    getcontext(&ctx1); 
    printf("%d\n", idx); 
    idx++; 
    sleep(1); 
    setcontext(&ctx1); 
    return 0; 
} 
./01  
0 
1 
2 
. 
. 
. 

例2、用makecontext/(setcontext/swapcontext)实现函数调用

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

void fun() { 
    printf("this is fun...\n"); 
} 

int main() { 
    ucontext_t ctx1; 
    getcontext(&ctx1); 
    ctx1.uc_stack.ss_sp = malloc(4096); 
    ctx1.uc_stack.ss_size = 4096; 
    ctx1.uc_link = nullptr; 
    makecontext(&ctx1, fun, 0); 
    //setcontext(&ctx1); 
    ucontext_t ctx2; 
    swapcontext(&ctx2, &ctx1); 
    return 0; 
} 

输出:

./02  
this is fun... 

例3、ucontext簇函数实现函数循环调用

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

ucontext_t fun_ctx, test_ctx; 

void test(); 

void fun() { 
    printf("this is %s...\n", __FUNCTION__); 
    sleep(1); 
    setcontext(&test_ctx); 
} 

void test() { 
    printf("this is %s...\n", __FUNCTION__); 
    sleep(1); 
    setcontext(&fun_ctx); 
} 

int main() { 
    getcontext(&test_ctx); 
    getcontext(&fun_ctx); 

    test_ctx.uc_stack.ss_sp = malloc(4096); 
    test_ctx.uc_stack.ss_size = 4096; 
    test_ctx.uc_link = &fun_ctx; 
    makecontext(&test_ctx, test, 0); 

    fun_ctx.uc_stack.ss_sp = malloc(4096); 
    fun_ctx.uc_stack.ss_size = 4096; 
    fun_ctx.uc_link = &test_ctx; 
    makecontext(&fun_ctx, fun, 0); 

    test(); 
    return 0; 
} 
./03 
this is test... 
this is fun... 
this is test... 
this is fun... 
this is test...