实验内容网址:https://xv6.dgs.zone/labs/requirements/lab7.html

本实验的代码分支:https://gitee.com/dragonlalala/xv6-labs-2020/tree/thread2/

Uthread: switching between threads

关键点:线程切换、swtch

思路:

本实验完成的任务为用户级线程系统设计上下文切换机制。在进行本实验前需要仔细阅读11.3 XV6线程切换(一) | MIT6.S081和11.4 XV6线程切换(二) | MIT6.S081这两个文档。从一个线程切换到另一个线程需要保存旧线程的CPU寄存器,并恢复新线程先前保存的寄存器;栈指针和程序计数器被保存和恢复的事实意味着CPU将切换栈和执行中的代码。用户寄存器存在trapframe中,内核线程的寄存器存在context中。在本实验中需要保存和恢复的寄存器是位于context结构体中的寄存器。

为什么RISC-V中有32个寄存器,但是swtch函数中只保存并恢复了14个寄存器?

答:因为switch是按照一个普通函数来调用的,对于有些寄存器,swtch函数的调用者默认swtch函数会做修改,所以调用者已经在自己的栈上保存了这些寄存器,当函数返回时,这些寄存器会自动恢复。所以swtch函数里只需要保存Callee Saved Register就行。因为swtch函数是从C代码调用的,所以我们知道Caller Saved Register会被C编译器保存在当前的栈上。Caller Saved Register大概有15-18个,而我们在swtch函数中只需要处理C编译器不会保存,但是对于swtch函数又有用的一些寄存器。所以在切换线程的时候,我们只需要保存Callee Saved Register。

步骤&代码:

首先定义一个tcontext结构体,结构体的内容与内核中context结构体的内容相同,这里为什么不使用内核中已经定义好的context结构体?因为这是一个用户程序,不好使用内核使用的数据结构。 ra(Return Address)寄存器保存的是当前函数的返回地址,sp(stack pointer)寄存器表示栈顶指针。

struct tcontext {

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 tcontext my_context; // 线程切换时需要保存的寄存器

};

在线程建立时初始化ra和sp寄存器。将ra 寄存器赋值为func后,函数会跳转到func函数处去执行。sp寄存器赋值为线程的栈顶。不太懂这里为什么是+STACK_SIZE?

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

t->

参考链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。