ucontext函数说明

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
发布了113 篇原创文章 · 获赞 22 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/woai110120130/article/details/100174805