上下文跳转之ucontext
之前有写过基于C语言标准库的跳转:使用setjmp,这里的ucontext算是第二种方法把
setjmp和longjmp
相关函数
- getcontext
int getcontext(ucontext_t *ucp);
The function getcontext() initializes the structure pointed at by ucp to the currently active context.
getcontext函数将ucp指针指向的结构初始化为当前上下文。
- setcontext
int setcontext(const ucontext_t *ucp);
The function setcontext() restores the user context pointed at by ucp. A successful call does not return. The context should have been obtained by a call of getcontext(), or makecontext(), or passed as third argument to asignal handler.
函数setcontext会恢复ucp指向的上下文。如果呼叫成功就不会返回。被指向的上下文应该是通过getcontext或者makecontext获得的,或者作为第三个参数传递给信号处理函数。
- swapcontext
int swapcontext(ucontext_t *oucp, const 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指向的上下文。
- makecontext
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
The makecontext() function modifies the context pointed to by ucp.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.
makecontext函数修改ucp指向的上下文。在调用makecontext之前,调用方必须为该上下文分配一个新堆栈,并将其地址分配给ucp->uc_stack,并定义一个后续上下文,将其地址指定给ucp–>uc_link。
代码演示
#include <stdio.h> #include <ucontext.h> #include <fcntl.h> ucontext_t main_ctx; ucontext_t func_ctx[3]; int count = 0; void func1() { while(count++ <= 30) { printf("func1 begin\r\n"); swapcontext(&func_ctx[0], &main_ctx); printf("func1 end\r\n"); } } void func2() { while(count++ <= 30) { printf("func2 begin\r\n"); swapcontext(&func_ctx[1], &main_ctx); printf("func2 end\r\n"); } } void func3() { while(count++ <= 30) { printf("func3 begin\r\n"); swapcontext(&func_ctx[2], &main_ctx); printf("func3 end\r\n"); } } int main() { char stack1[2048] = {0}; char stack2[2048] = {0}; char stack3[2048] = {0}; getcontext(&func_ctx[0]); func_ctx[0].uc_stack.ss_size = sizeof(stack1); func_ctx[0].uc_stack.ss_sp = stack1; func_ctx[0].uc_link = &main_ctx; makecontext(&func_ctx[0], func1, 0); getcontext(&func_ctx[1]); func_ctx[1].uc_stack.ss_sp = stack2; func_ctx[1].uc_stack.ss_size = sizeof(stack2); func_ctx[1].uc_link = &main_ctx; makecontext(&func_ctx[1], func2, 0); getcontext(&func_ctx[2]); func_ctx[2].uc_stack.ss_sp = stack3; func_ctx[2].uc_stack.ss_size = sizeof(stack3); func_ctx[2].uc_link = &main_ctx; makecontext(&func_ctx[2], func3, 0); while(count <= 30) { swapcontext(&main_ctx, &func_ctx[count%3]); count++; } return 0; }
运行结果:
代码解析:
从上面的运行截图可以看出,代码会在swapcontex处从arg1上下文切换到arg2上下文。然后会阻塞在那里,直到遇到下一个swapcontext。
总结
ucontext跳转同样适合用于协程,比起setjmp。它的使用更加简单,但是跨平台能力欠缺,比如在windows上就无法执行。