makecontext, swapcontext - 操作用户上下文
#include <ucontext.h>
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
int swapcontext(ucontext_t *restrict oucp,const ucontext_t *restrict ucp);
在类System V的系统中,在<ucontext.h>中定义了两种数据类型:mcontext_t ,ucontext_t 以及四个函数:getcontext(), setcontext(), makecontext(3),swapcontext(3) 以实现用户级线程在其控制进程中进行上下文切换。mcontext_t 类型依赖于CPU硬件且不透明,ucontext_t 类型是一个至少具有以下字段的结构,详见:ucontext(3) get/setcontext(3) :
typedef struct ucontext_t {
struct ucontext_t *uc_link;
sigset_t uc_sigmask;
stack_t uc_stack;
mcontext_t uc_mcontext;
...
} ucontext_t;
makecontext() 函数修改 ucp 指向的上下文(可以通过调用 getcontext(3) 获得),用于指定上下文执行时的入口点函数。 在调用 makecontext() 之前,调用者必须为此上下文分配一个新堆栈并将其地址分配给 ucp->uc_stack,并定义一个后继上下文并将其地址分配给 ucp->uc_link。
getcontext()获得的上下文保存的栈和入口执行点都是原线程的,makecontext()将会为执行上下文指定新的入口点,但是执行堆栈和后继上下文还需要手动指定。
当此上下文被执行时(使用 setcontext(3) 或 swapcontext()),函数 func 被调用,并在参数argc之后传递的一系列整数 (int) 参数; 调用者必须在 argc 中指定这些参数的数量。 当这个函数返回时,后继上下文(即ucp->uc_link)被调用执行。 如果后继上下文指针为 NULL,则线程退出。
swapcontext()函数将当前上下文保存在 oucp 指向的结构中,然后执行 ucp 指向的上下文。
成功后,swapcontext() 不会返回。(但是也可能返回,如 oucp 被调用执行,在这种情况下它看起来像是从 swapcontext() 返回 0,但是这与真正意义上的函数返回是不一样。)出现错误时,swapcontext() 返回 -1 并设置 errno 以指示错误。
ENOMEM
剩余堆栈空间不足。
自 2.1 版以来,glibc 中提供了 makecontext() 和 swapcontext()。
Interface | Attribute | Value |
---|---|---|
makecontext() | Thread safety | MT-Safe race:ucp |
swapcontext() | Thread safety | MT-Safe race:oucp race:ucp |
SUSv2,POSIX.1-2001。 POSIX.1-2008 删除了 makecontext() 和 swapcontext() 的规范,引用了可移植性问题,并建议重写应用程序以改用 POSIX 线程。
ucp->uc_stack 的解释和 sigaltstack(2) 一样,即这个结构体包含了作为栈使用的内存区域的起始和长度,与栈的增长方向无关。 因此,用户程序不必担心这个方向。
在 int 和指针类型大小相同的体系结构上(例如,x86-32,其中两种类型都是 32 位),您可能能够在 argc 之后将指针作为参数传递给 makecontext()。 但是,这样做并不能保证是可移植的,根据标准是未定义的,并且不适用于指针大于整数的体系结构。 尽管如此,从 2.8 版开始,glibc 对 makecontext() 进行了一些更改,以允许在某些 64 位架构(例如 x86-64)上执行此操作。
下面的示例程序演示了 getcontext(3)、makecontext() 和 swapcontext() 的使用。 运行该程序会产生以下输出:
$ ./a.out
main: swapcontext(&uctx_main, &uctx_func2)
func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: started
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: returning
func1: returning
main: exiting
源代码:
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
static ucontext_t uctx_main, uctx_func1, uctx_func2;
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
static void
func1(void)
{
printf("func1: started\n");
printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n");
if (swapcontext(&uctx_func1, &uctx_func2) == -1)
handle_error("swapcontext");
printf("func1: returning\n");
}
static void
func2(void)
{
printf("func2: started\n");
printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n");
if (swapcontext(&uctx_func2, &uctx_func1) == -1)
handle_error("swapcontext");
printf("func2: returning\n");
}
int
main(int argc, char *argv[])
{
char func1_stack[16384];
char func2_stack[16384];
if (getcontext(&uctx_func1) == -1)
handle_error("getcontext");
uctx_func1.uc_stack.ss_sp = func1_stack;
uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
uctx_func1.uc_link = &uctx_main;
makecontext(&uctx_func1, func1, 0);
if (getcontext(&uctx_func2) == -1)
handle_error("getcontext");
uctx_func2.uc_stack.ss_sp = func2_stack;
uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
/* Successor context is f1(), unless argc > 1 */
uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1;
makecontext(&uctx_func2, func2, 0);
printf("main: swapcontext(&uctx_main, &uctx_func2)\n");
if (swapcontext(&uctx_main, &uctx_func2) == -1)
handle_error("swapcontext");
printf("main: exiting\n");
exit(EXIT_SUCCESS);
}
网友评论