Lab: Multithreading
前置知识
阅读xv6 book 第七章
Uthread: switching between threads
实验目的
在本实验中,你需要设计用户级线程切换的上下文
uthread.c 包含了用户级线程切换的代码
uthread_switch.S 是你要是实现的保存和恢复线程上下文代码
实验实现
hints
- 在thread_creat() 和 thread_schedule() 中添加你的代码
- 修改 struct thread 来保存你的上下文
- 你将会在 thread_schedule() 中调用 thread_switch() --该函数应该在 uthread_switch.S 中实现
- thread_switch() 只需要保存和还原唤醒线程和被唤醒线程的上下文
实现思路
这里我们主要实现的就是一个 切换上下文 的功能,我们先来看看上下文 是怎么样的。在linux里面,线程和进程在数据结构上是没有区别的,线程就是共享资源的几个进程,这样我们先到xv6的进程里看看进程的数据结构。
/* proc.c */
struct proc {
struct spinlock lock;
.......
struct context context; // swtch() here to run process
.......
};
// Saved registers for kernel 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 的东西,看起来是我们要的上下文,直接把它搬到uthread.c 里面去(似乎没有程序计数器pc?不管了,可能存在下面的某个寄存器里?)
/* uthread.c */
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 thread {
char stack[STACK_SIZE]; /* the thread's stack */
int state; /* FREE, RUNNING, RUNNABLE */
struct context context;
//save pc of stack?
};
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;
t->context.ra = (uint64)func;//执行完thread_switch()后会返回到这里
t->context.sp = (uint64)(t->stack + STACK_SIZE);//栈从高地址往低地址增长
// YOUR CODE HERE
}
void
thread_schedule(void)
{
......
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->context)), (uint64)(&(current_thread->context)));
} else
next_thread = 0;
}
然后根据计组实验时对xv6指令模糊记忆,去写thread_switch() 的汇编代码
/* uthread_switch.S */
.text
/*
* save the old thread's registers,
* restore the new thread's registers.
*/
.globl thread_switch
thread_switch:
/* YOUR CODE HERE */
/* 每个uint64 大小为8个字节,按照偏移量储存然后再载入就行*/
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 */
剩下两个在linux上使用多线程的实验就不细说了,对锁和条件变量的简单应用