In a System V-like environment, one has the type ucontext_t defined in <ucontext.h> and the four functions get-context(2), setcontext(2), makecontext() and swapcontext() that allow user-level context switching between multi-ple threads of control within a process.
在系統V環境中,我們可以通過getcontext,setcontext,makecontext,swapcontext函數族,在單進程中實現使用者態內容相關的切換。
一、認識ucontext
首先我們來認識一下這個結構中的文鍵變數,結構如下,需要關注到的是uc_stack,以及uc_link,uc_stack指向環境切換需要儲存的棧空間,需要使用者指派。uc_link指向當時上下文執行函數成功退出後,指定啟用的上下文,如果為uc_link為NULL時則直接退出。
typedef struct ucontext{ unsigned long int uc_flags; struct ucontext *uc_link; stack_t uc_stack; mcontext_t uc_mcontext; __sigset_t uc_sigmask; struct _fpstate __fpregs_mem;} ucontext_t;
二、認識函數族
int getcontext(ucontext_t *ucp);
getcontext(2) gets the current context of the calling process, storing it in the ucontext struct pointed to by ucp.
getcontext把當前的上下文儲存在ucp中
int setcontext(const ucontext_t *ucp);
setcontext(2) sets the context of the calling process to the state stored in the ucontext struct pointed to by ucp. The struct must either have been created by getcontext(2) or have been passed as the third parameter of the sigaction(2) signal handler.
setcontext把當前進程恢複到ucp指標指向的上下文,ucp必須由getcontext建立
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
The makecontext() function modifies the context pointed to by ucp (which was obtained from a call to getcon-text(2)). 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.
When this context is later activated (using setcontext(2) or swapcontext()) the function func is called, and passed the series of integer (int) arguments that follow argc; the caller must specify the number of these argu-ments in argc. When this function returns, the successor context is activated. If the successor context pointer is NULL, the thread exits.
makecontext修改ucp指標指向的上下文,在調用makecontext之前,必須為ucp對象分配一個新棧並賦為ucp->uc_stack,同時定義一個成功返回後恢複的上下文並把地址賦給ucp->uc_link。當ucp被啟用後(通過setcontext或swapcontext啟用),func函數會被調用,並把一系統參數傳凝視給func函數。當func函數返回原來,uc_link會被啟用,uc_link為NULL時,線程退出。
int swapcontext(ucontext_t *oucp, 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。
三、樣本
3.1 MAN手冊樣本
#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 voidfunc1(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 voidfunc2(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");} intmain(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_func2; 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);}
輸出如下
:
func2: startedfunc2: swapcontext(&uctx_func2, &uctx_func1)func1: startedfunc1: swapcontext(&uctx_func1, &uctx_func2)func2: returningfunc1: returningmain: exiting
3.2 生產者消費者
#include <ucontext.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>static ucontext_t uc_loop, uc_main, uc_consume, uc_produce;static int ci, pi, loop;static void cb_consume(){ printf("consume %d\n", ci); ci++; sleep(1);}static void cb_produce(){ printf("produce %d\n", pi); pi++;}int main(void){ char consume_stack[1024], produce_stack[1024]; getcontext(&uc_loop); getcontext(&uc_produce); uc_produce.uc_stack.ss_sp = produce_stack; uc_produce.uc_stack.ss_size = sizeof(produce_stack); uc_produce.uc_link = &uc_consume; makecontext(&uc_produce, cb_produce, 0); printf("finish make produce\n"); getcontext(&uc_consume); uc_consume.uc_stack.ss_sp = consume_stack; uc_consume.uc_stack.ss_size = sizeof(consume_stack); uc_consume.uc_link = &uc_main; makecontext(&uc_consume, cb_consume, 0); printf("finish make consume\n"); printf("swap main to produce\n"); swapcontext(&uc_main, &uc_produce); printf("swap back to main\n"); sleep(1); loop++; if (loop < 2) setcontext(&uc_loop); return 0;}
輸出如下:
finish make producefinish make consumeswap main to produceproduce 0consume 0swap back to mainfinish make producefinish make consumeswap main to produceproduce 1consume 1swap back to main