ucontext用于在用户空间切换函数运行上下文。
什么是函数运行上下文,首先一个函数运行的必要环境包括
1调用函数,也就是函数运行结束后返回哪里
2函数的栈,返回地址一般被保存在栈上
3 函数的代码
来看下深入理解计算机系统中的一张图
这图可以说明栈的情况。
现在我们来看下ucontext提供的四个函数,和他们的主要意图
/* Get user context and store it in variable pointed to by UCP. */
extern int getcontext (ucontext_t *__ucp) __THROWNL;
/* Set user context from information of variable pointed to by UCP. */
extern int setcontext (const ucontext_t *__ucp) __THROWNL;
/* Save current context in context variable pointed to by OUCP and set
context from variable pointed to by UCP. */
extern int swapcontext (ucontext_t *__restrict __oucp,
const ucontext_t *__restrict __ucp) __THROWNL;
/* Manipulate user context UCP to continue with calling functions FUNC
and the ARGC-1 parameters following ARGC when the context is used
the next time in `setcontext' or `swapcontext'.
We cannot say anything about the parameters FUNC takes; `void'
is as good as any other choice. */
extern void makecontext (ucontext_t *__ucp, void (*__func) (void),
int __argc, ...) __THROW;
getcontext 其实很好理解,用于获取当前上下文
setcontext 则用于切换上下文(上下文用ucontext_t结构表示)
比如下面一段代码
#include <stdio.h>
#include <ucontext.h>
#include <stdlib.h>
int main(void)
{
ucontext_t uc;
int i = 0;
printf("before getcontext\n");
getcontext(&uc);
printf("get context finish\n");
i++;
if (i < 5) {
setcontext(&uc);
}
printf("set context finist\n");
}
使用getcontext函数获取当前上下文, 当前上下文是什么?其实就是上面的一个栈的各种寄存器地址(这包含下一条要执行的地址寄存器ip,栈指针sp,和以及各种其他寄存器),当我们使用setcontext进行切换上下文的时候,寄存器(代码地址,栈地址,计算中间结果)都被恢复了(注意计算的中间结果一部分在栈上,一部分在寄存器中,恢复栈指针就相当于恢复栈的上的中间结果,恢复其他寄存器就是相当于恢复了另外一部分中间结果, 恢复ip则保证函数可以向下执行)。所以setcontext可行的条件是当前的栈不被破坏。
所以函数的输出结果如下
before getcontext
get context finish
get context finish
get context finish
get context finish
get context finish
set context finist
这里给读者提个问题,为什么i的值会累加(提示getcontext保存现场的时候i的值在哪里)
另外怎么理解makecontext呢,getcontext是从当前函数获取函数运行的上下文,makecontext则是要自己创造一个上下文,所以我们要提供哪些信息?
就是最开始说的那个三要素:代码,栈和调用函数,举个例子
int main(void)
{
ucontext_t uc, old_context;
int i = 0;
printf("before getcontext\n");
getcontext(&uc);
getcontext(&old_context);
printf("get context finish\n");
i++;
uc.uc_link = &old_context;
uc.uc_stack.ss_sp = malloc(1<<20);
uc.uc_stack.ss_size = 1<<20;
makecontext(&uc, trampoline, 2, 1, 2);
if (i < 5) {
setcontext(&uc);
}
printf("set context finist\n");
}
输出结果如下
before getcontext
get context finish
trampoline arg1 = 1, arg2 = 2
get context finish
trampoline arg1 = 1, arg2 = 2
get context finish
trampoline arg1 = 1, arg2 = 2
get context finish
trampoline arg1 = 1, arg2 = 2
get context finish
set context finist
makecontext创建了一个新的上下文,新上下文的ip寄存器地址指向trampoline, sp指向我们分配的栈内存,uc_link参数则是用于指定返回的上下文(如果不指定返回的u_link其实也是可以的,函数执行完毕就退出了,没有返回地址,意味着他是一个顶层函数,这里其实就是操作栈上的返回地址一栏)。
swapcontext函数的作用则其实就是getcontext和setcontext的组合,把当前上下文保存到第一个参数中,然后切到第二个上下文执行。
int main(void)
{
ucontext_t uc, old_context;
int i = 0;
printf("before getcontext\n");
getcontext(&uc);
printf("get context finish\n");
i++;
uc.uc_link = &old_context;
uc.uc_stack.ss_sp = malloc(1<<20);
uc.uc_stack.ss_size = 1<<20;
makecontext(&uc, trampoline, 2, 1, 2);
if (i < 5) {
swapcontext(&old_context, &uc);
}
printf("set context finist\n");
}
输出如下
before getcontext
get context finish
trampoline arg1 = 1, arg2 = 2
set context finist