在类System V的环境中,可以通过定义在<ucontext.h>头文件中的两个数据结构:mcontext_t 和 ucontext_t ,以及四个接口,getcontext(), setcontext(), makecontext(), swapcontext(),来实现用户级别的上下文切换。
mcontext_t类型是机器相关的且不透明。 这ucontext_t类型是至少具有以下内容的结构 :
typedef struct ucontext_t {
struct ucontext_t *uc_link;
sigset_t uc_sigmask;
stack_t uc_stack;
mcontext_t uc_mcontext;
...
} ucontext_t;
在<signal.h>中定义了sigset_t和stack_t。 uc_link 指向当前 上下文终止时要运行的下一个上下文,uc_sigmask是屏蔽信号集 ,uc_stack是此对象使用的堆栈 上下文,而uc_mcontext是计算机上下文:器寄存器 。
#include <ucontext.h>
int getcontext(ucontext_t* ucp);
int setcontext(const ucontext_t* ucp);
void makecontext(ucontext_t* ucp, void (*func)(), int argc, ...);
int swapcontext(ucontext_t* olducp, ucontext_t* newucp);
int getcontext(ucontext_t * ucp);
- 获取当前上下文, 初始化ucp结构体, 将当前上下文保存到ucp中
- 成功返回0; 失败返回-1, 并设置errno
void makecontext(ucontext_t *ucp, void(*func)(), int argc, ...);
- 创建一个目标上下文 创建方式: (1) getcontext, (2) 指定分配给上下文的栈
uc_stack.ss_sp
, (3) 指定这块栈的大小uc_stack.ss_size
, (4) 指定uc_stack.ss_flags
, (5) 指定后继上下文uc_link
- 协程运行时使用主协程划分的栈空间,而协程切回主线程时需要将该部分栈空间的内容copy到每个协程各自的一个空间缓存起来,因为主协程中划分的栈空间并不是只用于一个协程,而是会用于多个协程
- makecontext可以修改通过getcontext初始化得到的上下文, (必须先调用getcontext), 然后为ucp指定一个栈空间
ucp->stack
, 设置后继的上下文ucp->uc_link
- 当上下文通过setcontext或者swapcontext激活后, 执行func函数(argc为后续的参数个数, 可变参数). 当func执行返回后, 继承的上下文被激活(
ucp->uc_link
), 如果为NULL, 则线程退出。
int setcontext(const ucontext_t *ucp)
- 设置当前的上下文为ucp(激活ucp)
- ucp来自getcontext, 那么上下文恢复至ucp
- ucp来自makecontext, 那么将会调用makecontext函数的第二个参数指向的函数func, 如果func返回, 则恢复至
ucp->uc_link
指定的后继上下文, 如果该ucp中的uc_link
为NULL, 那么线程退出 - 成功不返回, 失败返回-1, 设置errno
int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
- 切换上下文
- 保存当前上下文至oucp, 激活ucp上下文(先执行makecontext指定的ucp入口函数, 而后会执行
ucp->uc_link
指向的后继上下文) - 成功不返回, 失败返回-1, 设置errno