git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
cd rCore-Tutorial-v3
git checkout ch8
包含一个应用程序
user/src/bin/
├── stackful_coroutine.rs
执行这个应用程序
Rust user shell
>> stackful_coroutine
stackful_coroutine begin...
TASK 0(Runtime) STARTING
TASK 1 STARTING
task: 1 counter: 0
TASK 2 STARTING
task: 2 counter: 0
TASK 3 STARTING
task: 3 counter: 0
TASK 4 STARTING
task: 4 counter: 0
...
提纲
2.1 实践步骤
2.3 用户态管理的线程控制接口和实现
参考:
简单的用户态管理多线程应用 stackful_coroutine.rs
pub fn main() {
let mut runtime = Runtime::new(); //创建线程管理子系统
runtime.init(); // 初始化线程管理子系统
runtime.spawn(|| { //创建一个用户态线程
println!("TASK 1 STARTING");
let id = 1;
for i in 0..4 {
println!("task: {} counter: {}", id, i);
yield_task(); //主动让出处理器
}
println!("TASK 1 FINISHED");
}); //... 继续创建第2~4个用户态线程
runtime.run(); //调度执行各个线程
}
struct Task { //线程控制块
id: usize,
stack: Vec<u8>,
ctx: TaskContext,
state: State,
}
pub struct TaskContext { //线程上下文
x1: u64, //ra: return addres
x2: u64, //sp
..., //s[0..11] 寄存器
nx1: u64, //new return addres
}
struct Task { //线程控制块
id: usize,
stack: Vec<u8>,
ctx: TaskContext,
state: State,
}
enum State { //线程状态
Available,
Running,
Ready,
}
提纲
2.1 实践步骤
2.2 用户态管理的线程结构
参考:
Runtime::new() 主要有三个步骤:
Runtime::init()
把Rutime结构变量的地址赋值给全局可变变量RUNTIME
,以便在后续执行中会根据RUNTIME
找到对应的Runtime结构变量。
在应用的 main()
函数中,首先会依次调用上述两个函数(new和init),完成线程管理运行时的初始化过程。这样正在运行的TID为 0 的主线程就可代表线程运行时进行后续创建线程等一系列工作。
pub fn spawn(&mut self, f: fn()) { // f函数是线程入口
let available = self
.tasks.iter_mut() //遍历队列中的任务
.find(|t| t.state == State::Available) //查找可用的任务
.expect("no available task.");
let size = available.stack.len();
unsafe {
let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
let s_ptr = (s_ptr as usize & !7) as *mut u8; // 栈按8字节对齐
available.ctx.x1 = guard as u64; //ctx.x1 is old return address
available.ctx.nx1 = f as u64; //ctx.nx2 is new return address
available.ctx.x2 = s_ptr.offset(-32) as u64; //cxt.x2 is sp
}
available.state = State::Ready; //设置任务为就绪态
}
}
x1
寄存器:老的返回地址 -- guard
函数地址nx1
寄存器:新的返回地址 -- 输入参数 f
函数地址x2
寄存器:新的栈地址 -- available.stack+sizefn guard() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_return();
};
}
fn t_return(&mut self) {
if self.current != 0 {
self.tasks[self.current].state = State::Available;
self.t_yield();
}
}
guard
函数意味着传入的f
函数(线程的主体)已经返回,线程已完成运行任务,进而取消引用我们的运行时并调用t_return()。
当应用要切换线程时,会调用 yield_task 函数,通过 runtime.t_yield 函数来完成具体的切换过程。runtime.t_yield()
函数主要完成的功能:
Ready
,把新就绪线程的状态改为Running
,把 runtime 的 current 设置为新就绪线程控制块的idfn t_yield(&mut self) -> bool {
...
self.tasks[pos].state = State::Running;
let old_pos = self.current;
self.current = pos;
unsafe {
switch(&mut self.tasks[old_pos].ctx, &self.tasks[pos].ctx);
}
...
unsafe fn switch(old: *mut TaskContext, new: *const TaskContext) {
// a0: _old, a1: _new
asm!("
sd x1, 0x00(a0)
...
sd x1, 0x70(a0)
ld x1, 0x00(a1)
...
ld t0, 0x70(a1)
jr t0
...
pub fn run(&mut self){
while self.t_yield() {}
println!("All tasks finished!");
}
提纲
3.2 内核态管理的线程控制接口
3.3 线程管理与进程管理
3.4 内核态管理的线程的实现
参考:
pub struct ProcessControlBlockInner {
pub tasks: Vec<Option<Arc<TaskControlBlock>>>,
...
}
git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
cd rCore-Tutorial-v3
git checkout ch8
包含几个与内核态管理的用户线程相关的应用程序
user/src/bin/
├── threads.rs
├── threads_arg.rs
Rust user shell
>> threads_arg
aaa...bbb...ccc...aaa...bbb...ccc...
thread#1 exited with code 1
thread#2 exited with code 2
ccc...thread#3 exited with code 3
main thread exited.
...
提纲
3.1 实践步骤
3.3 线程管理与进程管理
3.4 内核态管理的线程的实现
参考:
简单的内核态管理多线程应用 threads_arg.rs
fn thread_print(arg: *const Argument) -> ! { //线程的函数主体
...
exit(arg.rc)
}
pub fn main() -> i32 {
let mut v = Vec::new();
for arg in args.iter() {
v.push(thread_create( thread_print, arg )); //创建线程
...
for tid in v.iter() {
let exit_code = waittid(*tid as usize); //等待线程结束
...
}
进程运行过程中,可创建多个属于这个进程的线程,每个线程有自己的线程标识符(TID,Thread Identifier)。
系统调用 thread_create 的原型:
/// 功能:当前进程创建一个新的线程
/// 参数:entry 表示线程的入口函数地址
/// 参数:arg:表示线程的一个参数
pub fn sys_thread_create(entry: usize, arg: usize) -> isize
线程执行完代表它的功能后,会通过 exit
系统调用退出。进程/主线程调用 waittid
来回收其资源,来彻底销毁整个线程。
系统调用 waittid
的原型:
/// 参数:tid表示线程id
/// 返回值:如果线程不存在,返回-1;如果线程还没退出,返回-2;其他情况下,返回结束线程的退出码
pub fn sys_waittid(tid: usize) -> i32
提纲
3.1 实践步骤
3.2 内核态管理的线程控制接口
3.4 内核态管理的线程的实现
参考:
引入了线程机制后,进程相关的重要系统调用:fork 、 exec 、 waitpid 接口上没有变化,但完成的功能上需要有一定的扩展。
进程相关的这三个系统调用还是保持了已有的进程操作的语义,并没有由于引入了线程,而带来大的变化
问题:“被fork的子进程是否要复制父进程的多个线程?”
目前的rcore tutorial ,选择了C,简化了应用的使用场景,即在使用fork和create_thread(以及基于线程的信号量,条件变量等)是不会同时出现的。如果有fork,假定是这个应用是单线程的进程,所以只拷贝了这个单线程的结构。这种简化设计虽然是一种鸵鸟做法,但也避免了一些允许fork和create_thread共存而导致的比较复杂的情况:...
场景:在fork前,有三个线程Main thread, thread X, thread Y, 且Thread X拿到一个lock,在临界区中执行;Thread Y正在写一个文件。Main thread执行fork.
提纲
3.1 实践步骤
3.2 内核态管理的线程控制接口
3.3 线程管理与进程管理
参考:
改进现有进程管理的一些数据结构包含的内容及接口,把进程中与处理器相关的部分分拆出来,形成线程相关的部分。
pub struct TaskControlBlock {
pub process: Weak<ProcessControlBlock>, //线程所属的进程控制块
pub kstack: KernelStack,//任务(线程)的内核栈
inner: UPSafeCell<TaskControlBlockInner>,
}
pub struct TaskControlBlockInner {
pub res: Option<TaskUserRes>, //任务(线程)用户态资源
pub trap_cx_ppn: PhysPageNum,//trap上下文地址
pub task_cx: TaskContext,//任务(线程)上下文
pub task_status: TaskStatus,//任务(线程)状态
pub exit_code: Option<i32>,//任务(线程)退出码
}
pub struct ProcessControlBlock {
pub pid: PidHandle,
inner: UPSafeCell<ProcessControlBlockInner>,
}
pub struct ProcessControlBlockInner {
pub tasks: Vec<Option<Arc<TaskControlBlock>>>,
pub task_res_allocator: RecycleAllocator,
...
}
RecycleAllocator是PidAllocator的升级版,即一个相对通用的资源分配器,可用于分配进程标识符(PID)和线程的内核栈(KernelStack)。
sys_thread_create
当一个进程执行中发出系统调用 sys_thread_create
后,操作系统就需要在当前进程的基础上创建一个线程,即在线程控制块中初始化各个成员变量,建立好进程和线程的关系等,关键要素包括:
sys_thread_create
pub fn sys_thread_create(entry: usize, arg: usize) -> isize {
// create a new thread
let new_task = Arc::new(TaskControlBlock::new(...
// add new task to scheduler
add_task(Arc::clone(&new_task));
// add new thread to current process
let tasks = &mut process_inner.tasks;
tasks[new_task_tid] = Some(Arc::clone(&new_task));
*new_task_trap_cx = TrapContext::app_init_context( //建立trap/task上下文
entry,
new_task_res.ustack_top(),
kernel_token(),
...
sys_exit
sys_exit
系统调用时,内核会调用 exit_current_and_run_next 函数退出当前线程并切换到下一个线程,但不会导致其所属进程的退出。sys_exit
pub fn sys_exit(exit_code: i32) -> ! {
exit_current_and_run_next(exit_code); ...
pub fn exit_current_and_run_next(exit_code: i32) {
let task = take_current_task().unwrap();
let mut task_inner = task.inner_exclusive_access();
drop(task_inner); //释放线程资源
drop(task); //释放线程控制块
if tid == 0 {
// 释放当前进程的所有线程资源
// 释放当前进程的资源
...
sys_waittid
sys_waittid
pub fn sys_waittid(tid: usize) -> i32 {
...
if let Some(waited_task) = waited_task {
if let Some(waited_exit_code) = waited_task.....exit_code {
exit_code = Some(waited_exit_code);
}
} else {
return -1; // waited thread does not exist
}
if let Some(exit_code) = exit_code {
process_inner.tasks[tid] = None; //dealloc the exited thread
exit_code
} else {
-2 // waited thread has not exited
}
达科塔盗龙Dakotaraptor是一种生存于距今6700万-6500万年前白垩纪晚期的兽脚类驰龙科恐龙,它主打的并不是霸王龙的力量路线,而是利用自己修 长的后肢来提高敏捷度和奔跑速度。它全身几乎都长满了羽毛,可能会滑翔或者其他接近飞行行为的行动模式。