相关知识
使用锁的例子(内存页空闲链表)

假设两个CPU同时要释放内存。假设CPU0先运行,那么CPU0会将它的变量r的next指向当前的freelist。如果CPU1在同一时间运行,它会将自己的变量r的next也指向当前的freelist。现在两个物理page对应的变量r都指向了同一个freelist(注,也就是原来单链表的head节点)。
因为我们这里只有一个内存,所以总是有一个CPU会先执行,另一个后执行。我们假设CPU0先执行,那么freelist(空闲内存页链表头)会等于CPU0的变量r。之后CPU1再执行,它又会将freelist更新为CPU1的变量r。这样的结果是,在之后的内存管理中,我们只知道CPU1的设置的变量r。我们丢失了CPU0对应的page。CPU0想要释放的内存page最终没有出现在freelist数据中。事实中,我们可能有更多的cpu,会发生更多奇怪的现象。
线程
xv6中一个进程只有一个线程,linux中一个进程可有多个线程
线程状态:
RUNNING,线程当前正在某个CPU上运行
RUNABLE,线程还没有在某个CPU上运行,但是一旦有空闲的CPU就可以运行
SLEEPING,这个状态意味着线程在等待一些I/O事件,它只会在I/O事件发生了之后运行
定时器中断。在每个CPU核上,都存在一个硬件设备,它会定时产生中断。定时器中断处理程序,将CPU出让(yield)给线程调度器。将一个RUNNING线程转换成一个RUNABLE线程
线程切换
当用户程序在运行时,实际上是用户进程中的一个用户线程在运行。如果程序执行了一个系统调用或者因为响应中断走到了内核中,那么相应的用户空间状态会被保存,同时属于这个用户程序的内核线程被激活。
在XV6中,任何时候都需要经历:
1.从一个用户进程切换到另一个用户进程,都需要从第一个用户进程接入到内核中,保存用户进程的状态并运行第一个用户进程的内核线程。
2.再从第一个用户进程的内核线程切换到第二个用户进程的内核线程。
3.之后,第二个用户进程的内核线程暂停自己,并恢复第二个用户进程的用户寄存器。
4.最后返回到第二个用户进程继续执行。
注意:
每一个CPU核在一个时间只会做一件事情,每个CPU核在一个时间只会运行一个线程,它要么是运行用户进程的线程,要么是运行内核线程,要么是运行这个CPU核对应的调度器线程。所以在任何一个时间点,CPU核并没有做多件事情,而是只做一件事情。线程的切换创造了多个线程同时运行在一个CPU上的假象。类似的每一个线程要么是只运行在一个CPU核上,要么它的状态被保存在context中。线程永远不会运行在多个CPU核上,线程要么运行在一个CPU核上,要么就没有运行。