> For the complete documentation index, see [llms.txt](https://rcore.gitbook.io/rust-os-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://rcore.gitbook.io/rust-os-docs/jin-cheng-guan-li-mo-kuai.md).

# 线程管理模块

【部分内容已经过时】模块经过了重构，支持多核调度

## 概述

线程管理模块的职责是完成CPU资源的管理。它建立在硬件提供的中断及上下文切换机制之上，提供线程的管理和调度功能。此模块功能相当于ucore lab4。

注意它不负责管理线程使用的其他资源（如内存、文件），也不了解用户态和内核态的切换（由使用者构造合适的上下文来解决）。目前的设计基于单核CPU，未来扩展到多核时可能需要重新调整。

具体而言，使用者只需实现底层的上下文切换过程，即可享用内核线程的功能。此模块提供了直接操作线程的接口，如fork、exit、wait，用于实现系统调用。在此基础上，还封装了和[`std::thread`](https://doc.rust-lang.org/std/thread/index.html)完全相同的上层接口，使得用户态的Rust多线程程序可以方便地迁移到内核中。

## 实现

此模块作为独立的crate存在，代码位于：`crate/process`

src目录结构及各文件功能如下：

```
.
├── lib.rs          根模块
├── processor.rs    一个CPU核的管理器
├── scheduler.rs    调度算法
├── thread.rs       实现std::thread接口
├── event_hub.rs    简单事件管理器
└── util.rs         其它工具
```

理论上可以在Linux用户态实现一个模拟的上下文切换，进而编写单元测试，但这个想法尚未实现。

### 上下文接口

位于`processor.rs`，只有一个很简单的trait：

{% code title="processor.rs" %}

```rust
pub trait Context: Debug {
    /// 将当前CPU切换到另一个上下文
    unsafe fn switch(&mut self, target: &mut Self);
    
    /// 构造一个新的内核态上下文
    /// 只用于实现thread::spawn
    fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Self;
}
```

{% endcode %}

这个上下文实际上是保存在内核栈上的寄存器和中断帧TrapFrame，切换switch过程实际就是一段保存和恢复寄存器的汇编代码。

读者可分别查看它在x86\_64和RISCV32下的实现，了解具体使用方法：

* arch/x86\_64/interrupt/trapframe.rs
* arch/riscv32/context.rs

为了创建一个新的线程，使用者需要分配好一个内核栈，并精心构造栈顶的初始数据，使得switch后能正确设置CPU状态。Context的构造函数需由使用者自己定义，不会在接口中体现。RustOS中实现了三种构造函数，分别用于创建内核线程、用户线程、fork已有线程。

这套接口是面向软件上下文切换设计的，如果CPU支持硬件切换上下文，可能实现会更简单。

关于上下文切换的细节和原理，可参考 [第一个进程 | xv6 中文文档](https://th0ar.gitbooks.io/xv6-chinese/content/content/chapter1.html)。

### 调度器

位于`scheduler.rs`，首先定义了接口：

{% code title="scheduler.rs" %}

```rust
/// 调度器接口
/// 本质是一个就绪态线程的优先队列
pub trait Scheduler {
    /// 添加一个就绪线程
    fn insert(&mut self, pid: Pid);
    /// 移除一个就绪线程
    fn remove(&mut self, pid: Pid);
    /// 选出下一个要执行的线程，但不移除它
    fn select(&mut self) -> Option<Pid>;
    /// 每隔固定时间调用此函数，更新当前线程的时间片信息
    /// 返回是否该线程时间片用完，需要调度
    fn tick(&mut self, current: Pid) -> bool;   // need reschedule?
    /// 设置线程优先级
    fn set_priority(&mut self, pid: Pid, priority: u8);
}
```

{% endcode %}

之后提供了两个调度算法的实现：时间片轮转（RR），Stride。

### Processor

位于`processor.rs`，其中包含以下结构：

* Status：枚举类型，保存线程状态
* Event：枚举类型，描述未来的调度事件
* WaitResult：枚举类型，作为返回值描述线程的等待状态
* Process：线程控制块
* Processor：完成实际CPU管理的主体

Processor的完整定义如下：

```rust
Processor_<T: Context, S: Scheduler>
```

使用时需指定T和S的实现类型，然后定义一个全局实例：

{% code title="process/mod.rs" %}

```rust
type Processor = Processor_<Context, StrideScheduler>;

// Once用来支持稍后手动初始化
// 这里的Mutex实际是一个修改后的SpinNoIrqLock
// 即在lock期间同时关闭中断的自旋锁
// 目的是防止中断时再中断，导致死锁
static PROCESSOR: Once<Mutex<Processor>> = Once::new();

// 初始化示意
fn process_init() {
    PROCESSOR.call_once(|| Mutex::new(
        // 构造一个实例
        Processor::new(
            // 当前第一个线程的Context，内容为空
            unsafe { Context::new_init() },
            StrideScheduler::new(5),
        )
    ));
}
```

{% endcode %}

创建新线程时，首先自己构造Context，然后add：

```rust
// 定义idle线程
extern fn idle(arg: usize) -> ! {
    loop {}
}
// 构造Context并添加到Processor
processor.add(Context::new_kernel(entry: idle, arg: 0));
```

Processor目前的设计是让【修改线程状态】和【执行线程切换】两种操作尽量分离。

具体而言，【执行线程切换】只能通过schedule函数进行，它会判断当前线程状态是否为Running，若不是，委托调度器选出下一个目标线程，执行切换，等到下次切换回来时退出schedule函数。

而其它大部分函数（除current\_wait\_for），只会【修改线程状态】，而不会执行线程切换。其中exit/kill/sleep/wakeup/yield\_now函数，会调用set\_status进行显式的状态修改；而tick函数，会根据当前时刻的调度事件，进行隐式的状态修改。

在原版ucore中，每个CPU核有一个专门的调度线程，进行线程调度时，首先切换到调度线程，然后再切换到下一个线程。而在RustOS中是没有调度线程的，源线程直接调用schedule函数切换到下一个线程，能减少一次上下文切换。这两种实现方式哪种更好，欢迎大家一起讨论。

### std::thread接口

位于`thread.rs`，首先定义了`ThreadSupport`接口提供底层支持，然后定义了一个空结构`ThreadMod<S: ThreadSupport>`实现和std::thread相同的上层接口。

由于这部分仅仅是在Processor外面又套了一层，因此所谓的`ThreadSupport`接口只需能访问到全局Processor即可。对使用者而言，需要进行以下操作：

```rust
// 首先我们已经有了一个Processor的全局实例
type Processor = Processor_<Context, StrideScheduler>;
pub static PROCESSOR: Once<Mutex<Processor>> = Once::new();

// 实现ThreadSupport接口
pub struct ThreadSupportImpl;
impl ThreadSupport for ThreadSupportImpl {
    type Context = Context;
    type Scheduler = StrideScheduler;
    type ProcessorGuard = MutexGuard<'static, Processor>;
    // 上面都是铺垫，下面才是重点
    fn processor() -> Self::ProcessorGuard {
        PROCESSOR.try().unwrap().lock()
    }
}

// ‘实例化’ThreadMod，并假装它是一个mod
#[allow(non_camel_case_types)]
pub type thread = ThreadMod<ThreadSupportImpl>;

// 然后就可以把它当作`std::thread`来用了
use thread;
let t = thread::current();

// 然而假的毕竟是假的……
let t: thread::Thread;   // Compile ERROR!
```

之所以用这样一种奇怪的实现方式，是为了这个需求：我希望实现一些全局静态函数，同时希望它们的底层依赖是可替换的。std::thread本身的实现方式是用条件编译，对不同系统使用不同的底层函数，然而我们这个没法这么搞。另一种可能的实现方式是用一个大宏把整个包起来，使用时再展开，不过这样IDE就无法实时分析这部分代码了……

至于thread本身的实现，除了spawn之外都比较简单，读者可查看源码了解实现细节。

## TODO

* [x] 多核支持
