实验四(上):线程
实验之前
- 阅读实验指导四。
- 从本次实验起,我们将不再提供“截至当前章节的代码框架”。你可以直接在
master
分支上查看代码,因为后面章节基本只会添加代码而鲜有修改。 - 实验用到的代码在
lab-4
分支上,与master
稍有修改。
实验题目
原理:线程切换之中,页表是何时切换的?页表的切换会不会影响程序 / 操作系统的运行?为什么?
Click to show页表是在
Process::prepare_next_thread()
中调用Thread::prepare()
,其中换入了新线程的页表。它不会影响执行,因为在中断期间是操作系统正在执行,而操作系统所用到的内核线性映射是存在于每个页表中的。
设计:如果不使用
sscratch
提供内核栈,而是像原来一样,遇到中断就直接将上下文压栈,请举出(思路即可,无需代码):- 一种情况不会出现问题
- 一种情况导致异常无法处理(指无法进入
handle_interrupt
) - 一种情况导致产生嵌套异常(指第二个异常能够进行到调用
handle_interrupt
,不考虑后续执行情况) - 一种情况导致一个用户进程(先不考虑是怎么来的)可以将自己变为内核进程,或以内核态执行自己的代码
Click to show- 只运行一个非常善意的线程,比如
loop {}
- 线程把自己的
sp
搞丢了,比如mv sp, x0
。此时无法保存寄存器,也没有能够支持操作系统正常运行的栈 - 运行两个线程。在两个线程切换的时候,会需要切换页表。但是此时操作系统运行在前一个线程的栈上,一旦切换,再访问栈就会导致缺页,因为每个线程的栈只在自己的页表中
- 用户进程巧妙地设计
sp
,使得它恰好落在内核的某些变量附近,于是在保存寄存器时就修改了变量的值。这相当于任意修改操作系统的控制信息
实验:当键盘按下 Ctrl + C 时,操作系统应该能够捕捉到中断。实现操作系统捕获该信号并结束当前运行的线程(你可能需要阅读一点在实验指导中没有提到的代码)
实验:实现进程的
fork()
。目前的内核线程不能进行系统调用,所以我们先简化地实现为“按 F 进行 fork”。fork 后应当为目前的进程复制一份几乎一样的拷贝。旧题目:这个题目有一些问题,会导致线程中对栈上的指针失效。如果已经完成了
clone()
实验,推荐但不必须重新做fork()
。实现线程的
clone()
。目前的内核线程不能进行系统调用,所以我们先简化地实现为“按 C 进行 clone”。clone 后应当为目前的线程复制一份几乎一样的拷贝,新线程与旧线程同属一个进程,公用页表和大部分内存空间,而新线程的栈是一份拷贝。