Transferred from: http://blog.csdn.net/cyberlabs/article/details/6920138
Implementing user threads with Makecontext modern UNIX systems provide functions for context switching in ucontext.h, which are GetContext, Setcontext,swapcontext, and Makecontext. Where GetContext is used to save the current context, SetContext is used to toggle the context, Swapcontext saves the current context and switches to another context, Makecontext creates a new context. The process of implementing a user thread is that we first call GetContext to get the current context and then modify ucontext_t to specify a new context. Similarly, we need to open up the stack space, but this implementation of the line libraries to involve the direction of the stack growth. We then call Makecontext to toggle the context and specify the function to execute in the user thread. Another challenge in this implementation is that a thread must be able to voluntarily yield the CPU to other threads. The Swapcontext function can accomplish this task, and Figure 4 shows a sample program of this implementation that the child thread and the parent thread are constantly switching to achieve multi-threaded effects. In Libfiber-the complete implementation can be seen in the uc.c file. #include #include #include//64kB Stack#defineFiber_stack 1024*64ucontext_t Child, parent;//The child thread would execute this functionvoidthreadfunction () {printf ("Child Fiber yielding to parent"); Swapcontext (&child, &parent);p rintf ("Child Thread exiting\n"); Swapcontext (&child, &parent);}intMain () {//Get The current execution contextGetContext (&Child );//Modify the context to a new stackChild.uc_link =0; child.uc_stack.ss_sp=malloc(fiber_stack); Child.uc_stack.ss_size=Fiber_stack;child.uc_stack.ss_flags=0; if(CHILD.UC_STACK.SS_SP = =0) {perror ("malloc:could not allocate stack"); exit (1 );}//Create The new contextprintf"Creating Child fiber\n"); Makecontext (&child, &threadfunction,0 );//Execute the child contextprintf"switching to child fiber\n"); Swapcontext (&parent, &Child );p rintf ("switching to child fiber again\n"); Swapcontext (&parent, &Child );//Free the stack Free(CHILD.UC_STACK.SS_SP);p rintf ("Child Fiber returned and stack freed\n" );return 0;} Figure 4 One of the most important factors for implementing a preemptive preemption implementation of thread user-level threads with Makecontext is the timer interrupt, which allows us to interrupt the execution of the current program, to asynchronously count the elapsed time slices of the process, and, if necessary, to run out of time slices, It is also possible that a high-priority program is ready to execute from the current process to other processes. For a user-space program, the signal that corresponds to the interrupt of the kernel space is the same as the interrupt, which is triggered asynchronously and can cause a jump in the execution stream. Therefore, in order to achieve user-level thread preemption, we can use the timer signal (SIGALRM), if necessary, in the signal processing program within the context of the switch. In order to verify my ideas, I added the relevant preemption code on the basis of the collaborative multithreading mentioned in the previous article, which is implemented as follows: #include<stdlib.h>#include<stdio.h>#include<ucontext.h>#include<sys/time.h>#defineStack_size 4096#defineUthread_max_num 256#defineInit_ticks 10typedefintUthread_t;typedefvoiduthread_attr_t;uthread_t Current=0;#defineUthread_self () Currentstructuthread_struct{intused; ucontext_t context; CharStack[stack_size]; void* (*func) (void*Arg); void*Arg; void*Exit_status; intticks;};Static structuthread_struct Uthread_slots[uthread_max_num];voidPanicvoid) {fprintf (stderr,"Panic, Bala bala...\n"); Exit (exit_failure);}voidIdle_thread (void){ inti; for(i =1; i < Uthread_max_num; i + +) if(uthread_slots[i].used) Break; if(i = =uthread_max_num) Panic (); if(Current! =0) uthread_slots[current].used=0; Current=i; Swapcontext (&uthread_slots[0].context,&uthread_slots[current].context);}voidUthread_context_init (inttid) {GetContext (&uthread_slots[tid].context); UTHREAD_SLOTS[TID].CONTEXT.UC_STACK.SS_SP=Uthread_slots[tid].stack; Uthread_slots[tid].context.uc_stack.ss_size=sizeof(Uthread_slots[tid].stack); Uthread_slots[tid].context.uc_link= &uthread_slots[0].context;}voidUthread_init (void) {Uthread_context_init (0); uthread_slots[0].used =1; Makecontext (&uthread_slots[0].context, Idle_thread,0);}voidUthread_schedule (void);voidUthread_exit (void*exit_status) {Uthread_slots[current].exit_status=Exit_status; Uthread_slots[current].used=0; Uthread_schedule ();}voidUthread_helper (void) {uthread_exit (Uthread_slots[current].func (Uthread_slots[current].arg));}intUthread_create (uthread_t *thread,Constuthread_attr_t *attr,void* (*start_routine) (void*),void*Arg) { Static intlast_used =0; inti; for(i = (last_used +1)% Uthread_max_num; I! =last_used; I= (i +1) %uthread_max_num)if(!uthread_slots[i].used) Break; if(i = =last_used)return-1; Last_used=i; if(Thread! =NULL)*thread =i; Uthread_context_init (i); Uthread_slots[i].used=1; Uthread_slots[i].func=Start_routine; Uthread_slots[i].arg=Arg; Uthread_slots[i].exit_status=0; Uthread_slots[i].ticks= Uthread_slots[current].ticks/2; Uthread_slots[current].ticks-=uthread_slots[i].ticks; Makecontext (&uthread_slots[i].context, Uthread_helper,0); return 0;}voidUthread_schedule (void){ intI, prev; for(i = (current +1)% Uthread_max_num; I! =Current ; I= (i +1) %uthread_max_num)if(uthread_slots[i].used) Break; if(i = =Current ) panic (); Prev=Current ; Current=i; Swapcontext (&uthread_slots[prev].context,&uthread_slots[current].context);}void* Thread (void*Arg) { inti; for(i =0;1; i + +) { ifI +==0) printf ("thread/%d (%s): i =%d\n", Current, (Char*) arg,i); Uthread_create (null, NULL, thread, ARG); ifI1000000==0) Uthread_schedule (); }}voidSig_ticks_timer (intSigno) { if(--uthread_slots[current].ticks <=0) {uthread_slots[current].ticks=init_ticks; Uthread_schedule (); }}intMainintargcChar*argv[]) {uthread_t tid; structItimerval Ticks_timer; Uthread_init (); Uthread_create (&tid, NULL, Thread,"HW1"); printf ("Tid is%d\n", Tid); Uthread_create (&tid, NULL, Thread,"hw2"); printf ("Tid is%d\n", Tid); Signal (SIGALRM, Sig_ticks_timer); Ticks_timer.it_interval.tv_sec=0; Ticks_timer.it_interval.tv_usec=10000; Ticks_timer.it_value.tv_sec=0; Ticks_timer.it_value.tv_usec=10000; Setitimer (Itimer_real,&Ticks_timer, NULL); while(1) Idle_thread (); return 0;} There seems to be some, maybe really can be in the user space to implement a virtual operating system environment to play it ...
Using Makecontext to implement user thread "Go"