state-machine
和event-driven programming
。每次调用switch,代码都会进行跳转,当内核再次调用switch并切换时,会跳转回来。
如果是从内核线程跳转至调度器线程,则一般是跳转至scheduler函数内;
如果是从调度器线程跳转至内核线程,则一般是跳转至内核线程执行switch函数的下一条语句。
内核第一次调用switch时,其中一个contex是伪造的,因为并不存在与之交换的contex。
https://pdos.csail.mit.edu/6.S081/2020/labs/thread.html
该实验的主要目标是实现用户级的线程管理和切换。相关代码位于user/uthread.c
文件中。该文件一共涉及4个线程,main
线程,thread_a
线程,thread_b
线程,thread_c
线程。需要注意的点是:
具体实现如下:
文件user/uthread.c
// 为每个线程设置contex,为了进行线程切换时保存寄存器的现场
struct thread {
char stack[STACK_SIZE]; /* the thread's stack */
int state; /* FREE, RUNNING, RUNNABLE */
struct context* context;
};
// 直接从内核的context拷贝过来
// Saved registers for context switches.
struct context {
uint64 ra;
uint64 sp;
// callee-saved
uint64 s0;
uint64 s1;
uint64 s2;
uint64 s3;
uint64 s4;
uint64 s5;
uint64 s6;
uint64 s7;
uint64 s8;
uint64 s9;
uint64 s10;
uint64 s11;
};
struct context all_context[MAX_THREAD];
文件user/uthread.c
void
thread_init(void)
{
// 增加初始化context的内容
for (int i = 0; i < MAX_THREAD; i++) {
all_thread[i].context = &all_context[i];
}
current_thread = &all_thread[0];
current_thread->state = RUNNING;
}
文件user/uthread.c
void
thread_schedule(void)
{
struct thread *t, *next_thread;
/* Find another runnable thread. */
next_thread = 0;
t = current_thread + 1;
for(int i = 0; i < MAX_THREAD; i++){
if(t >= all_thread + MAX_THREAD)
t = all_thread;
if(t->state == RUNNABLE) {
next_thread = t;
break;
}
t = t + 1;
}
if (next_thread == 0) {
printf("thread_schedule: no runnable threads\n");
exit(-1);
}
if (current_thread != next_thread) { /* switch threads? */
next_thread->state = RUNNING;
t = current_thread;
current_thread = next_thread;
/* YOUR CODE HERE
* Invoke thread_switch to switch from t to next_thread:
* thread_switch(??, ??);
*/
// +++++ begin +++++++
thread_switch((uint64)(t->context), (uint64)(next_thread->context));
// ------ end ----------
} else
next_thread = 0;
}
文件user/uthread.c
,更改thread_create
函数
void
thread_create(void (*func)())
{
struct thread *t;
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == FREE) break;
}
t->state = RUNNABLE;
// YOUR CODE HERE
memset(t->stack, 0, sizeof(char) * MAX_THREAD);
t->context->ra = (uint64)(*func);
// 注意栈的方向,否则会导致数据错乱
t->context->sp = (uint64)(&(t->stack[STACK_SIZE - 1]));
}
文件user/uthread_switch.S
.text
/*
* save the old thread's registers,
* restore the new thread's registers.
*/
.globl thread_switch
thread_switch:
/* YOUR CODE HERE */
sd ra, 0(a0)
sd sp, 8(a0)
sd s0, 16(a0)
sd s1, 24(a0)
sd s2, 32(a0)
sd s3, 40(a0)
sd s4, 48(a0)
sd s5, 56(a0)
sd s6, 64(a0)
sd s7, 72(a0)
sd s8, 80(a0)
sd s9, 88(a0)
sd s10, 96(a0)
sd s11, 104(a0)
ld ra, 0(a1)
ld sp, 8(a1)
ld s0, 16(a1)
ld s1, 24(a1)
ld s2, 32(a1)
ld s3, 40(a1)
ld s4, 48(a1)
ld s5, 56(a1)
ld s6, 64(a1)
ld s7, 72(a1)
ld s8, 80(a1)
ld s9, 88(a1)
ld s10, 96(a1)
ld s11, 104(a1)
ret /* return to ra */
该实验比较简单,主要目的是为Hashtable加锁,为了加快速度,这里我们主要在桶级别进行加锁。代码如下:
文件ph.c
,首先初始化锁
struct entry {
int key;
int value;
struct entry *next;
};
struct entry *table[NBUCKET];
// ++++ begin +++++++++
pthread_mutex_t locks[NBUCKET];
// ---- end ------------
// 增加函数初始化锁
void
initlock()
{
for (int i = 0; i < NBUCKET; i++) {
pthread_mutex_init(&locks[i], NULL);
}
}
// 在main函数中初始化锁
int
main(int argc, char *argv[])
{
assert(NKEYS % nthread == 0);
for (int i = 0; i < NKEYS; i++) {
keys[i] = random();
}
// +++++ begin ++++++
initlock();
// ------ end -------
//
// first the puts
//
t0 = now();
}
桶级加锁
static
void put(int key, int value)
{
int i = key % NBUCKET;
// is the key already present?
struct entry *e = 0;
pthread_mutex_lock(&locks[i]); // +++++
for (e = table[i]; e != 0; e = e->next) {
if (e->key == key)
break;
}
if(e){
// update the existing key.
e->value = value;
} else {
// the new is new.
insert(key, value, &table[i], table[i]);
}
pthread_mutex_unlock(&locks[i]); // +++++
}
结果
可以看到速度提升了,但是不到2倍。
该题主要是练习pthread_cond的使用,设置barrier主要目标是为了同步线程。
文件user/barrier.c
,更改函数barrier
为如下内容
static void
barrier()
{
// YOUR CODE HERE
//
// Block until all threads have called barrier() and
// then increment bstate.round.
//
pthread_mutex_lock(&bstate.barrier_mutex);
bstate.nthread++;
if (bstate.nthread == nthread) {
pthread_cond_broadcast(&bstate.barrier_cond);
bstate.nthread = 0;
bstate.round++;
} else {
// go to sleep on cond, releasing lock mutex, acquiring upon wake up
pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex);
}
pthread_mutex_unlock(&bstate.barrier_mutex);
}
执行结果
$ make grade
$ git commit -m "lab multithreading"
$ make handin
登录网站https://6828.scripts.mit.edu/2020/handin.py/student
,可以看到提交的结果。
https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081
https://pdos.csail.mit.edu/6.S081/2020/schedule.html
源码: https://github.com/aerfalwl/mit-xv6-labs-2020