Lab: Multithreading
课程地址:https://pdos.csail.mit.edu/6.828/2022/labs/thread.html
Uthread: switching between threads
You will need to add code to thread_create() and thread_schedule() in user/uthread.c, and thread_switch in user/uthread_switch.S. One goal is ensure that when thread_schedule() runs a given thread for the first time, the thread executes the function passed to thread_create(), on its own stack. Another goal is to ensure that thread_switch saves the registers of the thread being switched away from, restores the registers of the thread being switched to, and returns to the point in the latter thread’s instructions where it last left off. You will have to decide where to save/restore registers; modifying struct thread to hold registers is a good plan. You’ll need to add a call to thread_switch in thread_schedule; you can pass whatever arguments you need to thread_switch, but the intent is to switch from thread t to next_thread.
这个课程挺简单的看下switch.S. 就可以 ,然后线程switch切换不换改变大部分trampling中寄存器的值,(因为无论是中断还是异常进入/退出到的kernel space/user space switch发生之前trampline已经被设置了)
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 */
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(??, ??);
*/
thread_switch((uint64)t,(uint64)next_thread);
} else
next_thread = 0;
}
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
//the thread executes the function passed to thread_create(), on its own stack.
// alocate stack
malloc(STACK_SIZE);
t->ra=(uint64)func;
t->sp=(uint64)&t->stack[STACK_SIZE-1]; //栈指针是从高地址向低地址增长的
}
reg | name | saver | description
-------+-------+--------+------------
x0 | zero | | hardwired zero
x1 | ra | caller | return address
x2 | sp | callee | stack pointer
x3 | gp | | global pointer
x4 | tp | | thread pointer
x5-7 | t0-2 | caller | temporary registers
x8 | s0/fp | callee | saved register / frame pointer
x9 | s1 | callee | saved register
x10-11 | a0-1 | caller | function arguments / return values
x12-17 | a2-7 | caller | function arguments
x18-27 | s2-11 | callee | saved registers
x28-31 | t3-6 | caller | temporary registers
pc | | | program counter
*
————————————————
大概原理讲解就是在发生switch被调用时(yield/定时调度)在uthread_switch.S 中会切换新旧线程的上下文,然后在保存了寄存器值后通过 ra 访问调用的func。
Using threads
In this assignment you will explore parallel programming with threads and locks using a hash table. You should do this assignment on a real Linux or MacOS computer (not xv6, not qemu) that has multiple cores. Most recent laptops have multicore processors.
static
void put(int key, int value)
{
int i = key % NBUCKET;
// is the key already present?
struct entry *e = 0;
for (e = table[i]; e != 0; e = e->next) {
if (e->key == key)
break;
}
pthread_mutex_lock(&lock);
if(e){
// update the existing key.
e->value = value;
} else {
// the new is new.
insert(key, value, &table[i], table[i]);
}
pthread_mutex_unlock(&lock);
}
static struct entry*
get(int key)
{
int i = key % NBUCKET;
struct entry *e = 0;
pthread_mutex_lock(&lock);
for (e = table[i]; e != 0; e = e->next) {
if (e->key == key) break;
}
pthread_mutex_unlock(&lock);
return e;
}
这个就按照说的在get ,put 加上锁就好了。
Barrier
In this assignment you’ll implement a barrier: a point in an application at which all participating threads must wait until all other participating threads reach that point too. You’ll use pthread condition variables, which are a sequence coordination technique similar to xv6’s sleep and wakeup.
static void
barrier()
{
// YOUR CODE HERE
//
// Block until all threads have called barrier() and
// then increment bstate.round.
//
pthread_mutex_lock(&bstate.barrier_mutex);
round++;
if(round==nthread){
bstate.round++;
round=0;
pthread_cond_broadcast(&bstate.barrier_cond); // wake up every thread sleeping on cond
}else{
pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex); // go to sleep on cond, releasing lock mutex, acquiring upon wake up
}
pthread_mutex_unlock(&bstate.barrier_mutex);
}