Skip to content

Commit

Permalink
7.2
Browse files Browse the repository at this point in the history
  • Loading branch information
MeouSker77 committed Mar 18, 2023
1 parent 34e8307 commit 23e18c2
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 0 deletions.
Binary file added imgs/f7-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions src/ch07.tex
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,40 @@ \section{多路复用}
xv6尝试尽可能简单地解决这些问题,但最终的代码仍然很棘手。

\section{代码:上下文切换}
\autoref{f7-1}概述了从一个进程切换到另一个进程的步骤:一个用户到内核的切换(通过系统调用或者中断)到达旧进程的内核线程,一个上下文切换到达当前CPU的调度器线程,一个上下文切换到新进程的内核线程,一个自陷返回到用户级的进程。
xv6的每个CPU的调度器有一个专用的线程(有自己的寄存器和栈),因为让调度器在旧进程的内核栈上执行是不安全的:其他的核心可能会唤醒这个进程然后运行它,然后在两个核心上同时使用同一个栈将会导致灾难。
在这一节中我们将研究在内核线程和调度器线程之间切换的机制。

\begin{figure}[htbp]
\centering
\includegraphics[width=0.8\textwidth]{../imgs/f7-1.png}
\caption{从一个用户进程切换到另一个。在这个例子中,xv6运行在一个CPU上(因此只有一个调度器线程)。}
\label{f7-1}
\end{figure}

从一个线程切换到另一个涉及到保存旧线程的CPU寄存器和恢复新线程之前保存的寄存器。
栈指针和程序计数器需要保存和恢复意味着CPU需要切换栈和正在执行的代码。

函数\texttt{swtch}进行内核线程切换时的保存和恢复操作。
\texttt{swtch}并不考虑线程,它只是保存和切换32个RISC-V寄存器,这些寄存器被称为\emph{上下文(context)}。
当进程需要放弃CPU时,进程的内核线程会调用\texttt{swtch}保存它自己的上下文并返回到调度器的上下文。
每个上下文都被存储在一个\texttt{struct context}\href{https://github.com/mit-pdos/xv6-riscv/blob/risc/kernel/proc.h#L2}{(kernel/proc.h:2)}中,这个结构体本身也被包含在进程的\texttt{struct proc}和CPU的\texttt{struct cpu}中。
\texttt{swtch}有两个参数:\texttt{struct context *old}和\texttt{struct context *new}。
它把当前的寄存器保存在\texttt{old}中,从\texttt{new}中获取值加载到寄存器里,然后返回。

让我们跟随\texttt{swtch}进入到调度器中。
我们在\autoref{ch04}中看到过有一种情况下中断的最后一步是\texttt{usertrap}调用\texttt{yield}。
\texttt{yield}再反过来调用\texttt{sched},这个函数会调用\texttt{swtch}来把当前的上下文保存在\texttt{p->context}中,然后切换到保存在\texttt{cpu->context}中的调度器的上下文\href{https://github.com/mit-pdos/xv6-riscv/blob/risc/kernel/proc.c#L497}{(kernel/proc.c:497)}。

\texttt{swtch}\href{https://github.com/mit-pdos/xv6-riscv/blob/risc/kernel/swtch.S#L3}{(kernel/swtch.S:3)}只保存被调者负责保存的寄存器,C编译器在调用者中生成代码来把调用者负责保存的寄存器保存到栈上。
\texttt{swtch}知道\texttt{struct context}中每个寄存器字段的便宜量。
它并不保存程序计数器,而是保存\texttt{ra}寄存器,它保存了\texttt{swtch}结束时应该返回的地址。
现在\texttt{swtch}要根据新的上下文恢复寄存器,新的上下文是新线程之前调用\texttt{swtch}时保存的寄存器。
\texttt{swtch}返回时,它会返回到恢复的\texttt{ra}寄存器指向的地址,即新线程之前调用\texttt{swtch}时的返回地址。
另外,它会返回到新线程的栈,因为\texttt{sp}寄存器也会被恢复。

在我们的例子中,\texttt{sched}调用\texttt{swtch}来切换到每个CPU的调度器上下文\texttt{cpu->context}。
这个上下文是\texttt{scheduler}上一次调用\texttt{swtch}\href{https://github.com/mit-pdos/xv6-riscv/blob/risc/kernel/proc.c#L463}{(kernel/proc.c:463)}切换到现在正要放弃CPU的进程的时候保存下来的。
当我们正在追踪的\texttt{swtch}返回时,它会返回到\texttt{scheduler}而不是\texttt{sched},并且栈指针现在指向当前CPU的调度器栈。

\section{代码:调度}

0 comments on commit 23e18c2

Please sign in to comment.