基础概念
系统调用
1 2 3 4
|
pub fn sys_fork() -> isize;
|
1 2 3 4 5 6 7
|
pub fn sys_waitpid(pid: isize, exit_code: *mut i32) -> isize;
|
在一个子进程结束后通常由父进程调用 waitpid
回收资源并获取返回值。
若父进程先于子进程结束,则所有子进程的父进程转为用户初始进程(根节点)
1 2 3 4 5
|
pub fn sys_exec(path: &str) -> isize;
|
实际传入内核的 path
只有起始指针,需要手动在末尾添加
\0
对系统调用的简单封装:
1 2 3 4 5 6
|
pub fn wait(exit_code: &mut i32) -> isize; pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize;
|
读取文件的系统调用:sys_read
:
1 2 3 4 5 6 7 8
|
pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize;
pub fn read(fd: usize, buf: &mut [u8]) -> isize; pub fn getchar() -> u8;
|
用户初始程序:initproc
shell
程序:user_shell
核心数据结构
1 2 3 4 5 6 7 8
| static ref APP_NAMES: Vec<&'static str>;
pub fn get_app_data_by_name(name: &str) -> Option<&'static [u8]>;
pub fn list_apps();
|
使用 RAII 方法包装,支持自动回收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| pub struct PidHandle(pub usize);
struct PidAllocator { current: usize, recycled: Vec<usize>, }
impl PidAllocator { pub fn new() -> Self; pub fn alloc(&mut self) -> PidHandle; pub fn dealloc(&mut self, pid: usize); }
static ref PID_ALLOCATOR : UPSafeCell<PidAllocator>;
pub fn pid_alloc() -> PidHandle;
|
在 Lab2 中按照应用编号排布,在 Lab3 中改为以 PID 标识与排布
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| pub struct KernelStack { pid: usize, }
pub fn kernel_stack_position(app_id: usize) -> (usize, usize);
impl KernelStack { pub fn new(pid_handle: &PidHandle) -> Self;
pub fn get_top(&self) -> usize;
pub fn push_on_top<T>(&self, value: T) -> *mut T where T: Sized; }
|
管理一个进程的所有内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| pub struct TaskControlBlock { pub pid: PidHandle, pub kernel_stack: KernelStack, inner: UPSafeCell<TaskControlBlockInner>, }
pub struct TaskControlBlockInner { pub trap_cx_ppn: PhysPageNum, pub base_size: usize, pub task_cx: TaskContext, pub task_status: TaskStatus, pub memory_set: MemorySet, pub parent: Option<Weak<TaskControlBlock>>, pub children: Vec<Arc<TaskControlBlock>>, pub exit_code: i32, }
impl TaskControlBlockInner { pub fn get_trap_cx(&self) -> &'static mut TrapContext; pub fn get_user_token(&self) -> usize; fn get_status(&self) -> TaskStatus; pub fn is_zombie(&self) -> bool; }
impl TaskControlBlock { pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner>; pub fn getpid(&self) -> usize; pub fn new(elf_data: &[u8]) -> Self; pub fn exec(&self, elf_data: &[u8]) {...} pub fn fork(self: &Arc<TaskControlBlock>) -> Arc<TaskControlBlock> {...} }
|
为后续的扩展方便,将目前任务管理器对 CPU
的监控功能解耦,分到后面即将新增的结构 Processor
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| pub struct TaskManager { ready_queue: VecDeque<Arc<TaskControlBlock>>, }
impl TaskManager { pub fn new() -> Self; pub fn add(&mut self, task: Arc<TaskControlBlock>; pub fn fetch(&mut self) -> Option<Arc<TaskControlBlock>>; }
pub static ref TASK_MANAGER: UPSafeCell<TaskManager>;
pub fn add_task(task: Arc<TaskControlBlock>); pub fn fetch_task() -> Option<Arc<TaskControlBlock>>;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| pub struct Processor { current: Option<Arc<TaskControlBlock>>, idle_task_cx: TaskContext, }
pub static ref PROCESSOR: UPSafeCell<Processor>;
impl Processor { pub fn new() -> Self; fn get_idle_task_cx_ptr(&mut self) -> *mut TaskContext; pub fn take_current(&mut self) -> Option<Arc<TaskControlBlock>>; pub fn current(&self) -> Option<Arc<TaskControlBlock>>; }
pub fn take_current_task() -> Option<Arc<TaskControlBlock>>; pub fn current_task() -> Option<Arc<TaskControlBlock>>; pub fn current_user_token() -> usize; pub fn current_trap_cx() -> &'static mut TrapContext;
|
Processor
有一个单独的 idle
控制流,用于在空闲时从任务管理器获取进程
而当应用交出 CPU 使用权后,内核调用 schedule
切换回 idle
控制流:
1 2
| pub fn run_tasks(); pub fn schedule(switched_task_cx_ptr: *mut TaskContext);
|
管理机制实现
对所有内存信息进行复制,注意过程中细节,如父子关系维护
1 2 3 4 5 6 7 8 9 10 11 12 13
| impl MapArea { pub fn from_another(another: &MapArea) -> Self; }
impl MemorySet { pub fn from_existed_user(user_space: &MemorySet) -> Self; }
impl TaskControlBlock { pub fn fork(self: &Arc<TaskControlBlock>) -> Arc<TaskControlBlock>; }
pub fn sys_fork() -> isize;
|
1 2 3 4 5 6
| impl TaskControlBlock { pub fn exec(&self, elf_data: &[u8]); }
pub fn translated_str(token: usize, ptr: *const u8) -> String; pub fn sys_exec(path: *const u8) -> isize;
|
编程作业
实现 sys_spawn
其实就是把 fork
和 exec
缝在一起,实际实现的时候可以直接在 TaskControlBlock::new
的基础上稍稍修改(就是把父进程设为当前进程)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
pub fn sys_spawn(path: *const u8) -> isize { let token = current_user_token(); let path = translated_str(token, path); if let Some(data) = get_app_data_by_name(path.as_str()) { let task = current_task().unwrap().spawn(data); let pid = task.pid.0 as isize; add_task(task); pid } else { -1 } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
impl TaskControlBlock { pub fn spawn(self: &Arc<TaskControlBlock>, elf_data: &[u8]) -> Arc<TaskControlBlock> { let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data); let trap_cx_ppn = memory_set .translate(VirtAddr::from(TRAP_CONTEXT).into()) .unwrap() .ppn(); let mut parent_inner = self.inner_exclusive_access(); let pid_handle = pid_alloc(); let kernel_stack = KernelStack::new(&pid_handle); let kernel_stack_top = kernel_stack.get_top(); let task_control_block = Arc::new(TaskControlBlock { pid: pid_handle, kernel_stack, inner: unsafe { UPSafeCell::new(TaskControlBlockInner { trap_cx_ppn, base_size: parent_inner.base_size, task_cx: TaskContext::goto_trap_return(kernel_stack_top), task_status: TaskStatus::Ready, memory_set, parent: Some(Arc::downgrade(self)), children: Vec::new(), exit_code: 0, }) }, }); parent_inner.children.push(task_control_block.clone()); let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx(); *trap_cx = TrapContext::app_init_context( entry_point, user_sp, KERNEL_SPACE.exclusive_access().token(), kernel_stack_top, trap_handler as usize, ); task_control_block } }
|
stride 调度算法
在 TaskControlBlock
中加上 pass
和
priority
值,然后模拟就行,取任务的时候直接遍历一遍
fork
exec
spwan
对应的
pass
priority
值也要分别修改
为了防止溢出,我将 BIG_STRIDE
改为了
usize::MAX / 1048576
1 2 3 4 5 6 7 8 9
|
pub fn sys_set_priority(prio: isize) -> isize { if prio < 2 { return -1; } current_task().unwrap().inner_exclusive_access().priority = prio as usize; prio }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
impl TaskManager { pub fn fetch(&mut self) -> Option<Arc<TaskControlBlock>> { if self.ready_queue.is_empty() { return None; } let mut task_pos: usize = 0; for (idx, task) in self.ready_queue.iter().enumerate().skip(1) { if self.ready_queue[task_pos].inner_exclusive_access().pass > task.inner_exclusive_access().pass { task_pos = idx; } } let task = self.ready_queue.remove(task_pos).unwrap(); let mut inner = task.inner_exclusive_access(); inner.pass += BIG_STRIDE / inner.priority; drop(inner); Some(task) } }
|
前向兼容
基本就是把之前的代码照搬过来,然后对着修改适应现在的框架,如
mmap
之前在 TaskManager
中,现在改到
Processor
中,在 run_tasks()
中维护运行时间。
Github
Repository