多线程并发执行需要调度器的辅助。调度器的作用是在合适的时刻选择线程执行,并在合适的时候切换线程,防止一个线程占用或多的资源或阻塞,从而实现 cpu 资源分配相对“公平”。本章我们将:
假想我们已经有了一个调度算法。
算法和数据结构是分离的,所以在实现调度器的时候不依赖于算法
创建线程池(thread pool)用于保存所有线程。
创建线程管理器(processor)通过调度算法管理 thread pool。
实现线程调度相关函数。
引入 Round Robin 调度算法。
调度算法
//in process/scheduler.rsimplScheduler{pubfnnew(max_time_slice:usize)->Scheduler{/* TODO */}pubfnpush(&mutself,tid:Tid){/* TODO */}pubfnpop(&mutself)->Option<Tid>{/* TODO */}pubfntick(&mutself)->bool{/* TODO */}pubfnexit(&mutself,tid:Tid){/* TODO */}}
所以,不应该是这部分的问题。再进一步检查,在init.rs中的rust_main函数在调用process_init()之前,有一些unsafe代码,怀疑是它造成的???。试着把代码删除。再执行 make run ,发现我们的调度器已经能够自动切换线程,线程来回切换也可以正常恢复原先的工作环境,并且在线程结束后能够正常结束退出。
// in process/mod.rs
pub type Tid = usize;
pub type ExitCode = usize;
// in process/structs.rs
use crate::process::{ Tid, ExitCode };
#[derive(Clone)]
pub enum Status {
Ready,
Running(Tid),
Sleeping,
Exited(ExitCode),
}
// in process/thread_pool.rs
use crate::process::scheduler::Scheduler;
use crate::process::structs::*;
use alloc::{ vec::Vec, boxed::Box };
struct ThreadInfo {
status: Status,
present: bool,
thread: Option<Box<Thread>>,
}
pub struct ThreadPool {
threads: Vec<Option<ThreadInfo>>, // 线程信号量的向量
scheduler: Box<Scheduler>, // 调度算法
}
// in process/thread_pool.rs
use crate::process::Tid;
impl ThreadPool {
pub fn new(size: usize, scheduler: Scheduler) -> ThreadPool {
ThreadPool {
threads: {
let mut th = Vec::new();
th.resize_with(size, Default::default);
th
},
scheduler: Box::new(scheduler),
}
}
fn alloc_tid(&self) -> Tid {
for (i, info) in self.threads.iter().enumerate() {
if info.is_none() {
return i;
}
}
panic!("alloc tid failed !");
}
pub fn add(&mut self, _thread: Box<Thread>) {
let tid = self.alloc_tid();
self.threads[tid] = Some(ThreadInfo{
status: Status::Ready,
present: true,
thread: Some(_thread),
});
self.scheduler.push(tid);
println!("tid to alloc: {}", tid);
}
}
// in process/processor.rs
use core::cell::UnsafeCell;
use alloc::boxed::Box;
use crate::process::Tid;
use crate::process::structs::*;
use crate::process::thread_pool::ThreadPool;
pub struct ProcessorInner {
pool: Box<ThreadPool>,
idle: Box<Thread>,
current: Option<(Tid, Box<Thread>)>,
}
pub struct Processor {
inner: UnsafeCell<Option<ProcessorInner>>,
}
unsafe impl Sync for Processor {}
// in process/mod.rs
mod structs;
mod scheduler;
mod processor;
mod thread_pool;
use structs::Thread;
use alloc::boxed::Box;
use processor::Processor;
use thread_pool::ThreadPool;
use self::scheduler::Scheduler;
pub type Tid = usize;
pub type ExitCode = usize;
static CPU: Processor = Processor::new();
pub fn tick() {
CPU.tick();
}
pub fn init() {
println!("+------ now to initialize process ------+");
let scheduler = Scheduler::new(1);
let thread_pool = ThreadPool::new(100, scheduler);
CPU.init(Thread::new_idle(), Box::new(thread_pool));
let thread0 = Thread::new_kernel(hello_thread, 0);
CPU.add_thread(thread0);
let thread1 = Thread::new_kernel(hello_thread, 1);
CPU.add_thread(thread1);
let thread2 = Thread::new_kernel(hello_thread, 2);
CPU.add_thread(thread2);
let thread3 = Thread::new_kernel(hello_thread, 3);
CPU.add_thread(thread3);
let thread4 = Thread::new_kernel(hello_thread, 4);
CPU.add_thread(thread4);
CPU.run();
}
#[no_mangle]
pub extern "C" fn hello_thread(arg: usize) -> ! {
println!("hello thread");
println!("arg is {}", arg);
for i in 0..100 {
println!("{}{}{}{}{}{}{}{}", arg, arg, arg, arg, arg, arg, arg, arg);
for j in 0..1000 {
}
}
println!("end of thread {}", arg);
CPU.exit(0)
}
...
kernel_end:0x80c01000: kernel_size:0xc01000
+------ now to initialize process ------+
panicked at 'Processor is not initialized', /home/chyyuu/.rustup/toolchains/nightly-2019-03-05-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/option.rs:1038:5
qemu-system-riscv32: terminating on signal 15 from pid 31872 ()
// in process/mod.rs
...
static CPU: Processor = Processor::new();
pub fn init() {
println!("+------ now to initialize process ------+");
let scheduler = Scheduler::new(1);
let thread_pool = ThreadPool::new(100, scheduler);
println!("+------ now to initialize processor ------+");
CPU.init(Thread::new_idle(), Box::new(thread_pool));
...