您的位置:首页 > 其它

Baby's User-level Threads

2016-06-30 00:01 931 查看
This post shows the implementaion of a simple user-level thread package. The package contains only two source files: uthread.c and uthread_switch.s

uthread.c

#include <stdio.h>    // for printf()
#include <stdlib.h>   // for exit()

/* Possible states of a thread; */
#define FREE        0x0
#define RUNNING     0x1
#define RUNNABLE    0x2

#define STACK_SIZE  8192
#define MAX_THREAD  4

typedef struct thread thread_t, *thread_p;
typedef struct mutex mutex_t, *mutex_p;

struct thread {
int        sp;                /* curent stack pointer */
char       stack[STACK_SIZE];   /* the thread's stack */
int        state;             /* running, runnable, waiting */
};
static thread_t all_thread[MAX_THREAD];
thread_p  current_thread;
thread_p  next_thread;
extern void thread_switch(void);

void
thread_init(void)
{
// main() is thread 0, which will make the first invocation to
// thread_schedule().  it needs a stack so that the first thread_switch() can
// save thread 0's state.  thread_schedule() won't run the main thread ever
// again, because it is state is set to RUNNING, and thread_schedule() selects
// a RUNNABLE thread.
current_thread = &all_thread[0];
current_thread->state = RUNNING;
}

static void
thread_schedule(void)
{
thread_p t;

/* Find another runnable thread. */
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == RUNNABLE && t != current_thread) {
next_thread = t;
break;
}
}

if (t >= all_thread + MAX_THREAD && current_thread->state == RUNNABLE) {
/* The current thread is the only runnable thread; run it. */
next_thread = current_thread;
}

if (next_thread == 0) {
printf("thread_schedule: no runnable threads; deadlock\n");
exit(1);
}

if (current_thread != next_thread) {         /* switch threads?  */
next_thread->state = RUNNING;
thread_switch();
} else
next_thread = 0;
}

void
thread_create(void (*func)())
{
thread_p t;

for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == FREE) break;
}
t->sp = (int) (t->stack + STACK_SIZE);      // set sp to the top of the stack
t->sp -= 4;                              // space for return address
* (int *) (t->sp) = (int)func;             // push return address on stack
t->sp -= 32;                             // space for registers that thread_switch will push
t->state = RUNNABLE;
}

void
thread_yield(void)
{
current_thread->state = RUNNABLE;
thread_schedule();
}

static void
mythread(void)
{
int i;
printf("my thread running\n");
for (i = 0; i < 100; i++) {
printf("my thread 0x%x\n", (int) current_thread);
thread_yield();
}
printf("my thread: exit\n");
current_thread->state = FREE;
thread_schedule();
}

int
main(int argc, char *argv[])
{
thread_init();
thread_create(mythread);
thread_create(mythread);
thread_schedule();
return 0;
}


uthread_switch.s

.text

/* Switch from current_thread to next_thread. Make next_thread
* the current_thread, and set next_thread to 0.
* Use eax as a temporary register, which should be caller saved.
*/
.globl thread_switch
thread_switch:
pushal
movl current_thread, %eax
movl %esp, (%eax)              // save stack pointer of current thread
movl next_thread, %eax
movl %eax, current_thread
movl $0, next_thread
movl (%eax), %esp              // switch stack pointer to resume next thread
popal
ret                             // pop return address from stack


Compliing and running the program (gcc 4.8.5, CentOS Linux release 7.1.1503, x86-64)

gcc -m32 -o uthread uthread.c uthread_switch.s


$ ./uthread
my thread running
my thread 0x804c068
my thread running
my thread 0x804e070
my thread 0x804c068
my thread 0x804e070
...
my thread 0x804c068
my thread 0x804e070
my thread 0x804c068
my thread 0x804e070
my thread: exit
my thread: exit
thread_schedule: no runnable threads; deadlock


Discussion

The user-level thread package interacts badly with the operating system in several ways. For example, if one user-level thread blocks in a system call, another user-level thread won't run, because the user-level threads scheduler doesn't know that one of its threads has been descheduled by the OS's scheduler. As another example, two user-level threads will not run concurrently on different cores, because the OS scheduler isn't aware that there are multiple threads that could run in parallel. Note that if two user-level threads were to run truly in parallel, this implementation won't work because of several races (e.g., two threads on different processors could call thread_schedule concurrently, select the same runnable thread, and both run it on different processors.)

There are several ways of addressing these problems. One is using scheduler activations and another is to use one kernel thread per user-level thread (as Linux kernels do).
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: