增加exec, fork 等系统调用, 现在用户应用可以和os交互了

ch5
zhangxinyu 2 years ago
parent d1dc7056a5
commit 81f38f58c5

@ -57,7 +57,7 @@ pub fn rust_main(){
timer::set_next_trigger(); // 在进入用户态之前, 设置一个时钟中断, 防止第一个用户任务死循环 timer::set_next_trigger(); // 在进入用户态之前, 设置一个时钟中断, 防止第一个用户任务死循环
loader::list_apps(); loader::list_apps();
task::add_initproc(); task::add_initproc();
// task::run_tasks(); task::run_tasks();
panic!("Disable run here") panic!("Disable run here")
} }

@ -80,6 +80,11 @@ impl PhysAddr {
pub fn ceil(&self) -> PhysPageNum { pub fn ceil(&self) -> PhysPageNum {
PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE)
} }
// 把当前虚拟地址映射为指定类型T
pub fn get_mut<T>(&self) -> &'static mut T {
unsafe { (self.0 as *mut T).as_mut().unwrap() }
}
} }

@ -74,6 +74,15 @@ impl MapArea {
map_perm, map_perm,
} }
} }
// 根据当前逻辑段的信息, 创建另一个一样的逻辑段
pub fn from_another(another: &Self) -> Self {
Self {
vpn_range: VPNRange{l:another.vpn_range.l, r:another.vpn_range.r},
data_frames: BTreeMap::new(),
map_type: another.map_type,
map_perm: another.map_perm,
}
}
} }
impl MapArea { impl MapArea {
@ -460,4 +469,35 @@ impl MemorySet {
false false
} }
} }
// clone 当前的 地址空间 并返回一个新的, 注意虽然地址空间是相同的, 但是 他们的物理地址并不相同
pub fn from_existed_user(user_space: &Self) -> Self {
// 一个空的地址空间
let mut memory_set = Self::new();
// 映射跳板
memory_set.map_trampoline();
// copy data sections/trap_context/user_stack
// 拷贝数据
for area in user_space.areas.iter() {
// 根据 当前逻辑段的信息, 创建一个新的数据段
let new_area = MapArea::from_another(area);
// 把逻辑段信息 添加到当前地址空间(顺便也映射ppn)
memory_set.push(new_area, None);
// copy data from another space
// 把 当前逻辑段从开始到结束, ppn的数据, copy 到 新的 ppn
for vpn in area.vpn_range {
let src_ppn = user_space.page_table.get_pte(vpn).unwrap().ppn();
let dst_ppn = memory_set.page_table.get_pte(vpn).unwrap().ppn();
dst_ppn
.get_bytes_array()
.copy_from_slice(src_ppn.get_bytes_array());
}
}
memory_set
}
} }

@ -1,8 +1,9 @@
use alloc::string::String;
use alloc::vec; use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use bitflags::*; use bitflags::*;
use crate::config::TRAMPOLINE; use crate::config::TRAMPOLINE;
use crate::mm::address::{PhysPageNum, VirtAddr, VirtPageNum}; use crate::mm::address::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum};
use crate::mm::frame_allocator::{frame_alloc, FrameTracker}; use crate::mm::frame_allocator::{frame_alloc, FrameTracker};
use crate::println; use crate::println;
@ -172,6 +173,28 @@ impl PageTable {
pub fn get_pte(&self, vpn: VirtPageNum) -> Option<PageTableEntry> { pub fn get_pte(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
self.find_pte(vpn).map(|pte| *pte) self.find_pte(vpn).map(|pte| *pte)
} }
// 根据虚拟地址, 找到页表中对应的地址
pub fn get_pa(&self, va:VirtAddr) -> Option<PhysAddr>{
// vp对应的vpn
let vpn = va.floor();
// 找到对应vpn对应的pte
if let Some(pte) = self.get_pte(vpn) {
// 从pte找到 真实的物理页帧
let ppn = pte.ppn();
// 得到ppn的地址
let pa: PhysAddr = ppn.into();
let pa_usize: usize = pa.into();
// 虚拟地址对应的页内偏移
let va_offset = va.page_offset();
Some((pa_usize+va_offset).into())
} else {
None
}
}
} }
// 根据token得到用户应用的页表, 并根据ptr 和len 在页表中找到指定的数据 // 根据token得到用户应用的页表, 并根据ptr 和len 在页表中找到指定的数据
@ -214,4 +237,37 @@ pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&
v v
} }
// 根据token, 转换指定 虚拟地址空间中的指针ptr 为 指定 物理页帧内真实地址的 引用 &T
pub fn translated_refmut<T>(token: usize, ptr: *mut T) -> &'static mut T {
let page_table = PageTable::from_token(token);
let va = ptr as usize;
page_table
.get_pa(VirtAddr::from(va))
.unwrap()
.get_mut()
}
// 从token中映射虚拟地址页表, 并根据 虚拟地址ptr 逐个累加地址, 在物理页帧真实地址获得ptr后面的每个字符, 遇到 0 截止
pub fn translated_str(token: usize, ptr: *const u8) -> String {
let page_table = PageTable::from_token(token);
let mut string = String::new();
let mut va = ptr as usize;
loop {
let ch: u8 = *(page_table
.get_pa(VirtAddr::from(va))
.unwrap()
.get_mut()
);
if ch == 0 {
break;
} else {
string.push(ch as char);
va += 1;
}
}
string
}

@ -1,12 +1,47 @@
//! File and filesystem-related syscalls //! File and filesystem-related syscalls
use sbi_rt::legacy::console_getchar;
use crate::mm::page_table::translated_byte_buffer; use crate::mm::page_table::translated_byte_buffer;
use crate::print; use crate::print;
use crate::task::manager::TASK_MANAGER; use crate::task::manager::TASK_MANAGER;
use crate::task::processor::current_user_token; use crate::task::processor::current_user_token;
use crate::task::suspend_current_and_run_next;
const FD_STDOUT: usize = 1; const FD_STDOUT: usize = 1;
// 从标准输入读取 指定长度的字符到 虚拟地址buffer中, 目前只支持长度为1
pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize {
match fd {
FD_STDIN => {
assert_eq!(len, 1, "Only support len = 1 in sys_read!");
let mut c: usize;
loop {
// 利用sbi 从键盘获取输入的
c = console_getchar();
// 如果是0 说明还没有输入
if c == 0 {
// 切换进程
suspend_current_and_run_next();
continue;
} else {
break;
}
}
// 把读取的字符 写入到应用的地址空间
let ch = c as u8;
// 由于目前长度只能为1, 这里直接获取第0个slice 写入
let mut buffers = translated_byte_buffer(current_user_token(), buf, len);
unsafe {
buffers[0].as_mut_ptr().write_volatile(ch);
}
1
}
_ => {
panic!("Unsupported fd in sys_read!");
}
}
}
/// write buf of length `len` to a file with `fd` /// write buf of length `len` to a file with `fd`
pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
match fd { match fd {

@ -1,7 +1,13 @@
const SYSCALL_READ: usize = 63;
const SYSCALL_WRITE: usize = 64; const SYSCALL_WRITE: usize = 64;
const SYSCALL_EXIT: usize = 93; const SYSCALL_EXIT: usize = 93;
const SYSCALL_YIELD: usize = 124; const SYSCALL_YIELD: usize = 124;
const SYSCALL_GET_TIME: usize = 169; const SYSCALL_GET_TIME: usize = 169;
const SYSCALL_GETPID: usize = 172;
const SYSCALL_SBRK: usize = 214;
const SYSCALL_FORK: usize = 220;
const SYSCALL_EXEC: usize = 221;
const SYSCALL_WAITPID: usize = 260;
mod fs; mod fs;
mod process; mod process;
@ -14,11 +20,16 @@ use crate::println;
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
match syscall_id { match syscall_id {
SYSCALL_READ => sys_read(args[0], args[1] as *const u8, args[2]),
SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
SYSCALL_EXIT => sys_exit(args[0] as i32), SYSCALL_EXIT => sys_exit(args[0] as i32),
SYSCALL_YIELD => sys_yield(), SYSCALL_YIELD => sys_yield(),
SYSCALL_GET_TIME => sys_get_time(), SYSCALL_GET_TIME => sys_get_time(),
SYSCALL_SBRK => sys_sbrk(args[0] as i32), SYSCALL_SBRK => sys_sbrk(args[0] as i32),
SYSCALL_GETPID => sys_getpid(),
SYSCALL_FORK => sys_fork(),
SYSCALL_EXEC => sys_exec(args[0] as *const u8),
SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32),
_ => panic!("Unsupported syscall_id: {}", syscall_id), _ => panic!("Unsupported syscall_id: {}", syscall_id),
} }
} }

@ -1,8 +1,13 @@
//! App management syscalls //! App management syscalls
use alloc::sync::Arc;
use crate::loader::get_app_data_by_name;
use crate::mm::page_table::{translated_refmut, translated_str};
// use crate::batch::run_next_app; // use crate::batch::run_next_app;
use crate::println; use crate::println;
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next}; use crate::task::{exit_current_and_run_next, suspend_current_and_run_next};
use crate::task::processor::change_program_brk; use crate::task::manager::add_task;
use crate::task::processor::{change_program_brk, current_task, current_user_token};
use crate::task::task::TaskStatus;
// use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current_and_run_next}; // use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current_and_run_next};
use crate::timer::get_time_ms; use crate::timer::get_time_ms;
@ -30,3 +35,93 @@ pub fn sys_sbrk(size: i32) -> isize {
-1 -1
} }
} }
pub fn sys_getpid() -> isize {
current_task().unwrap().pid.0 as isize
}
pub fn sys_fork() -> isize {
// 创建一个会 跳到trap_return的子进程
let current_task = current_task().unwrap();
let new_task = current_task.fork();
let new_pid = new_task.pid.0;
// 修改子进程 trap_context 中的返回值, 因为父进程在调用sys_call中之后会立即返回
let trap_cx = new_task.inner_exclusive_access().get_trap_cx();
// 我们之前 进入这个函数的时候, 已经对子进程的 结束陷入的进行返回的地址 +4了
// 所以现在只需要改变子进程 陷入之后的返回值即可, 将来子进程会直接从 trap_return运行
trap_cx.x[10] = 0;
// 添加子任务到全局的 任务管理器
add_task(new_task);
// 返回 子进程的pid 给父进程
new_pid as isize
}
// 根据 文件名, 创建替换当前执行的任务
pub fn sys_exec(path: *const u8) -> isize {
// 当前token
let token = current_user_token();
// 得到 文件名
let path = translated_str(token, path);
// 根据文件名对应的二进制数据, 调用exec 替换
if let Some(data) = get_app_data_by_name(path.as_str()) {
let task = current_task().unwrap();
task.exec(data);
0
} else {
// 不存在 返回-1
-1
}
}
pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize {
// 当前进程 这里是父进程
let task = current_task().unwrap();
// find a child process
// 如果当前进程内没有对应pid的子进程 则返回-1, 如果传进来的pid是-1, 则表示任何一个子进程都是符合要求的
let mut inner = task.inner_exclusive_access();
if !inner
.children
.iter()
.any(|p| pid == -1 || pid as usize == p.get_pid())
{
return -1;
// ---- release current PCB
}
// 找到 指定进程且进程为 僵尸进程或者是任意进程
let pair = inner.children.iter().enumerate().find(|(_, p)| {
// ++++ temporarily access child PCB lock exclusively
p.inner_exclusive_access().task_status == TaskStatus::Zombie && (pid == -1 || pid as usize == p.get_pid())
// ++++ release child PCB
});
// 如果找到
if let Some((idx, _)) = pair {
// 从子列表中删除并取出
let child = inner.children.remove(idx);
// confirm that child will be deallocated after removing from children list
// 保证chlid当前是唯一存在的引用, 而不会出现在某个进程的子进程向量中, 更不会出现在处理器监控器或者任务管理器中, child在当前作用于结束后引用计数为0, 彻底删除,
// 作用域结束后, 内部的pid 和kernel stack 执行他们自己的drop方法(返还pid给pid管理器, 返还kernel stack的资源还给 KERNEL_SPACE, ), 还有 inner->memory_set->page_table->frames 三级页表本身所占的物理帧
assert_eq!(Arc::strong_count(&child), 1);
let found_pid = child.get_pid();
// ++++ temporarily access child TCB exclusively
// 找到子进程的退出码
let exit_code = child.inner_exclusive_access().exit_code;
// ++++ release child PCB
// 将退出码, 写入到当前进程的地址空间
let exit_code_ref = translated_refmut(inner.memory_set.token(), exit_code_ptr);
*exit_code_ref = exit_code;
// 返回回收的子进程的pid
found_pid as isize
} else {
// 如果制定进程不是僵尸进程 返回-2, 上层用户应用调用的 wait pid 会继续等待
-2
}
// ---- release current PCB lock automatically
}

@ -122,6 +122,100 @@ impl TaskControlBlock {
// base_size 用户栈顶距离0x0的距离, 即用户应用已知的大小 // base_size 用户栈顶距离0x0的距离, 即用户应用已知的大小
// //
} }
// 从当前进程copy 一个子进程, 他和父进程有相同的地址空间, 但是它拥有自己的内核栈 用来存放 trap_context
// 子进程后续运行会 条入到 trap_return
pub fn fork(self: &Arc<Self>) -> Arc<Self> {
// ---- 当前进程的内容
let mut parent_inner = self.inner_exclusive_access();
// 根据当前地址空间, copy 当前进程地址空间的数据到 一个新的地址空间
let memory_set = MemorySet::from_existed_user(&parent_inner.memory_set);
// 找到新的进程地址空间中 TRAP_CONTEXT 的物理页
let trap_cx_ppn = memory_set
.page_table
.get_pte(VirtAddr::from(TRAP_CONTEXT).into())
.unwrap()
.ppn();
// 分配一个pid
let pid_handle = pid_alloc();
// 根据pid 分配一个 子进程自己的内核栈
let kernel_stack = KernelStack::from(&pid_handle);
// 创建tcb
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 {
user_time: 0,
kernel_time: 0,
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,
heap_bottom: 0,
program_brk: 0,
})
},
});
// add child
// 添加子进程信息 到父进程中
parent_inner.children.push(task_control_block.clone());
// 得到 tcb 当中 trap context 的可变引用, 上面复制时还是复制的父进程的
// 现在修改其栈顶为我们新创建的 子进程自己的栈顶, 其他字段则是完全和父进程一样
let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx();
trap_cx.kernel_sp = kernel_stack_top;
// return
task_control_block
// ---- release parent PCB automatically
// **** release children PCB automatically
}
// TCB 加载一个新的应用, 从新的elf的 代码和数据替换 原有的旧的 应用地址空间的内容, 并开始执行
// pid 和 kernel_stack 不变
// 也不需要修改任务上下文环境, 因为只有被暂停的应用才需要再内核中保留一个任务上下文, 修改trap context 即可因
pub fn exec(&self, elf_data: &[u8]) {
// memory_set with elf program headers/trampoline/trap context/user stack
// 根据 elf data 创建一个地址空间
let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data);
let trap_cx_ppn = memory_set
.page_table
.get_pte(VirtAddr::from(TRAP_CONTEXT).into())
.unwrap()
.ppn();
// **** access inner exclusively
let mut inner = self.inner_exclusive_access();
// substitute memory_set
// 原有地址空间生命周期结束, 全部被回收
inner.memory_set = memory_set;
// 更新trap ppn
inner.trap_cx_ppn = trap_cx_ppn;
// 更新应用大小
inner.base_size = user_sp;
// 得到trap context 真实内存的指针
let trap_cx = inner.get_trap_cx();
*trap_cx = TrapContext::from(
entry_point,
user_sp,
KERNEL_SPACE.exclusive_access().token(),
self.kernel_stack.get_top(),
trap_handler as usize,
);
// 作用域结束 释放inner
// **** release inner automatically
}
} }
impl TaskControlBlockInner{ impl TaskControlBlockInner{

@ -6,6 +6,7 @@ use user_lib::{println, wait};
#[no_mangle] #[no_mangle]
fn main() -> i32 { fn main() -> i32 {
println!("1");
if sys_fork() == 0 { if sys_fork() == 0 {
// 注意需要手动添加\0 转为 c_str // 注意需要手动添加\0 转为 c_str
sys_exec("user_shell\0"); sys_exec("user_shell\0");

Loading…
Cancel
Save