Lab: Multithreading
背景知识
本练习将让您熟悉多线程。您将在用户级线程包中实现线程之间的切换,使用多个线程来加速程序,并实现barrier函数。
在编写代码之前,您应该确保已经阅读了xv6书中的“第7章:调度”,并研究和阅读了相应的代码
如果之前学过多线程以及线程处理函数就更好了!
Uthread:在线程之间切换(中等)
题目翻译
在本练习中,您将为用户级线程系统设计上下文切换机制,然后实现它。为了帮助您入门,xv6 有两个文件 user/uthread.c 和 user/uthread_switch.S,以及 Makefile 中的一条规则,用于构建一个 uthread 程序。uthread.c包含大部分用户级线程包,以及三个简单测试线程的代码。线程包缺少一些用于创建线程和在线程之间切换的代码。
您的工作是制定一个计划来创建线程并保存/恢复寄存器以在线程之间切换,并实施该计划。完成后,
make grade
应该说明您的解决方案通过了uthread
测试。
完成后,在 xv6 上运行 uthread
时,您应该会看到以下输出(三个线程可能以不同的顺序开始):
$ make qemu
...
$ uthread
thread_a started
thread_b started
thread_c started
thread_c 0
thread_a 0
thread_b 0
thread_c 1
thread_a 1
thread_b 1
...
thread_c 99
thread_a 99
thread_b 99
thread_c: exit after 100
thread_a: exit after 100
thread_b: exit after 100
thread_schedule: no runnable threads
$
这个输出来自三个测试线程,每个线程都有一个循环,打印一行,然后将CPU让给其他线程。
然而,在这一点上,由于没有上下文切换代码,你会看到没有输出。
你需要给user/uthread.c中的thread_create()和thread_schedule()以及user/uthread_switch.S中 thread_switch添加代码。一个目标是确保当thread_schedule()第一次运行一个给定的线程时,该线程在自己的堆栈中执行传递给thread_create()的函数。另一个目标是确保thread_switch保存被切换走的线程的寄存器,恢复被切换到的线程的寄存器,并返回到后一个线程的指令中它最后离开的地方。你必须决定在哪里保存/恢复寄存器;修改struct thread以保存寄存器是一个好计划。你需要在thread_schedule中添加对thread_switch的调用;你可以向thread_switch传递任何你需要的参数,但其意图是将线程t切换到下一个线程。
一些提示:
-
thread_switch只需要保存/恢复callee-save寄存器。为什么呢?
-
你可以在user/uthread.asm中看到uthread的汇编代码,这对于调试来说可能很方便。
-
为了测试你的代码,使用riscv64-linux-gnu-gdb单步浏览你的thread_switch可能会有帮助。你可以用这种方式开始:
(gdb) file user/_uthread
Reading symbols from user/_uthread...
(gdb) b uthread.c:60
这在uthread.c的第60行设置了一个断点。这个断点可能(也可能不)在你运行uthread之前就被触发了。这怎么可能发生呢?
一旦你的xv6 shell运行,输入 “uthread”,gdb就会在第60行中断。现在你可以键入类似下面的命令来检查uthread的状态。
(gdb) p/x *next_thread
用 “x”,你可以检查一个内存位置的内容。
(gdb) x/x next_thread->stack
你可以这样跳到thread_switch的开头。
(gdb) b thread_switch
(gdb) c
你可以使用单步汇编指令
(gdb) si
gdb的在线文档在这里。
题目答案
要实现在用户级线程之间进行切换,需要保存和恢复线程的寄存器,这里可以参考swtch的用法,注意这里需要在user/uthread_switch.S进行修改,之前一直没看清题目,于是在user/uthread_switch.asm 中就行添加代码。
1 .text
2
3 /*
4 * save the old thread's registers,
5 * restore the new thread's registers.
6 */
7
8 .globl thread_switch
9 thread_switch:
10 /* YOUR CODE HERE */
11
12 sd ra, 0(a0)
13 sd sp, 8(a0)
14 sd s0, 16(a0)
15 sd s1, 24(a0)
16 sd s2, 32(a0)
17 sd s3, 40(a0)
18 sd s4, 48(a0)
19 sd s5, 56(a0)
20 sd s6, 64(a0)
21 sd s7, 72(a0)
22 sd s8, 80(a0)
23 sd s9, 88(a0)
24 sd s10, 96(a0)
25 sd s11, 104(a0)
26
27 ld ra, 0(a1)
28 ld sp, 8