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
uthread_switch.s
Compliing and running the program (gcc 4.8.5, CentOS Linux release 7.1.1503, x86-64)
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).
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).
相关文章推荐
- iOS画板实现
- python django post提交403
- PHP MySQL递归查询
- Linux 时间、日期、时区
- Maven简介
- NoHttp网络请求框架简析——Android网络请求(二)
- velocity 参考文档
- 排名前50的开源爬虫
- Android学习--01-架构
- Python argparse 模块参考手册
- imagecreatefromjpegAllowed memory size of 13421772
- 推荐7个 CSS3 制作的创意下拉菜单效果
- css3选择器-选择范围
- whisper数据库
- graphite-web render api
- 如何清空rocketmq消息
- windows配置rocketmq开发环境(idea-eclipse)
- rocketmq稳定可靠性测试报告
- RocketMQ队列queue的偏移量Offset均衡分布测试
- RocketMQ消息堆积判断