Using Makecontext to implement user thread "Go"

Source: Internet
Author: User

Transferred from: http://blog.csdn.net/cyberlabs/article/details/6920138

Implementing a user thread using 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. The complete implementation can be seen in the libfiber-uc.c file.

#include
#include
#include

64kB Stack
#define Fiber_stack 1024*64

ucontext_t Child, parent;

The child thread would execute this function
void Threadfunction ()
{
printf ("Child fiber yielding to parent");
Swapcontext (&child, &parent);
printf ("Child thread exiting\n");
Swapcontext (&child, &parent);
}

int main ()
{
Get the current execution context
GetContext (&child);

Modify the context to a new stack
Child.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 context
printf ("Creating child fiber\n");
Makecontext (&child, &threadfunction, 0);

Execute the child context
printf ("Switching to Child fiber\n");
Swapcontext (&parent, &child);
printf ("Switching to Child fiber again\n");
Swapcontext (&parent, &child);

Free the stack
Free (CHILD.UC_STACK.SS_SP);

printf ("Child fiber returned and stack freed\n");

return 0;
}
Figure 4 Implementing a thread with Makecontext


preemption of user-level threads
One of the most important factors for preemption 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, the time slice, 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>

#define STACK_SIZE 4096
#define UTHREAD_MAX_NUM 256
#define Init_ticks 10

typedef int uthread_t;
typedef void uthread_attr_t;
uthread_t current = 0;

#define UTHREAD_SELF () current

struct UTHREAD_STRUCT
{
int used;
ucontext_t context;
Char Stack[stack_size];
void* (*func) (void *arg);
void *arg;
void *exit_status;
int ticks;
};

static struct uthread_struct uthread_slots[uthread_max_num];

void Panic (void)
{
fprintf (stderr, "Panic, Bala bala...\n");
Exit (Exit_failure);
}

void Idle_thread (void)
{
int i;

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);
}

void uthread_context_init (int tid)
{
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;
}

void Uthread_init (void)
{
Uthread_context_init (0);
uthread_slots[0].used = 1;
Makecontext (&uthread_slots[0].context, idle_thread, 0);
}

void Uthread_schedule (void);

void Uthread_exit (void *exit_status)
{
Uthread_slots[current].exit_status = Exit_status;
uthread_slots[current].used = 0;
Uthread_schedule ();
}

void Uthread_helper (void)
{
Uthread_exit (Uthread_slots[current].func (Uthread_slots[current].arg));
}

int Uthread_create (uthread_t *thread, const uthread_attr_t *attr,
void* (*start_routine) (void*), void *arg)
{
static int last_used = 0;
int i;

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;
}

void Uthread_schedule (void)
{
int I, 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)
{
int i;

for (i = 0; 1; i + +) {
if (i% 1000 = = 0)
printf ("thread/%d (%s): i =%d\n", Current, (char*) arg,i);
Uthread_create (null, NULL, thread, ARG);
if (i% 1000000 = = 0)
Uthread_schedule ();
}
}

void Sig_ticks_timer (int signo)
{
if (--uthread_slots[current].ticks <= 0) {
Uthread_slots[current].ticks = Init_ticks;
Uthread_schedule ();
}
}

int main (int argc, char *argv[])
{
uthread_t Tid;
struct Itimerval 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 ...

Use Makecontext to implement user thread "go"

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.