diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 9f2b2471..fa4a4cf1 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -7,6 +7,7 @@ const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; const SYSCALL_SLEEP: usize = 101; const SYSCALL_YIELD: usize = 124; +const SYSCALL_KILL: usize = 129; const SYSCALL_GET_TIME: usize = 169; const SYSCALL_GETPID: usize = 172; const SYSCALL_FORK: usize = 220; @@ -46,6 +47,7 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_EXIT => sys_exit(args[0] as i32), SYSCALL_SLEEP => sys_sleep(args[0]), SYSCALL_YIELD => sys_yield(), + SYSCALL_KILL => sys_kill(args[0], args[1] as u32), SYSCALL_GET_TIME => sys_get_time(), SYSCALL_GETPID => sys_getpid(), SYSCALL_FORK => sys_fork(), diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index 4184e231..a0c66590 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,8 +1,8 @@ use crate::fs::{open_file, OpenFlags}; use crate::mm::{translated_ref, translated_refmut, translated_str}; use crate::task::{ - current_process, current_task, current_user_token, exit_current_and_run_next, - suspend_current_and_run_next, + current_process, current_task, current_user_token, exit_current_and_run_next, pid2process, + suspend_current_and_run_next, SignalFlags, }; use crate::timer::get_time_ms; use alloc::string::String; @@ -103,3 +103,16 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { } // ---- release current PCB automatically } + +pub fn sys_kill(pid: usize, signal: u32) -> isize { + if let Some(process) = pid2process(pid) { + if let Some(flag) = SignalFlags::from_bits(signal) { + process.inner_exclusive_access().signals |= flag; + 0 + } else { + -1 + } + } else { + -1 + } +} diff --git a/os/src/task/manager.rs b/os/src/task/manager.rs index 542a0b87..fe37632b 100644 --- a/os/src/task/manager.rs +++ b/os/src/task/manager.rs @@ -1,6 +1,6 @@ -use super::TaskControlBlock; +use super::{ProcessControlBlock, TaskControlBlock}; use crate::sync::UPSafeCell; -use alloc::collections::VecDeque; +use alloc::collections::{BTreeMap, VecDeque}; use alloc::sync::Arc; use lazy_static::*; @@ -26,6 +26,8 @@ impl TaskManager { lazy_static! { pub static ref TASK_MANAGER: UPSafeCell = unsafe { UPSafeCell::new(TaskManager::new()) }; + pub static ref PID2PCB: UPSafeCell>> = + unsafe { UPSafeCell::new(BTreeMap::new()) }; } pub fn add_task(task: Arc) { @@ -35,3 +37,19 @@ pub fn add_task(task: Arc) { pub fn fetch_task() -> Option> { TASK_MANAGER.exclusive_access().fetch() } + +pub fn pid2process(pid: usize) -> Option> { + let map = PID2PCB.exclusive_access(); + map.get(&pid).map(|task| Arc::clone(task)) +} + +pub fn insert_into_pid2process(pid: usize, process: Arc) { + PID2PCB.exclusive_access().insert(pid, process); +} + +pub fn remove_from_pid2process(pid: usize) { + let mut map = PID2PCB.exclusive_access(); + if map.remove(&pid).is_none() { + panic!("cannot find pid {} in pid2task!", pid); + } +} diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 1e205400..7c16d820 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -3,6 +3,7 @@ mod id; mod manager; mod process; mod processor; +mod signal; mod switch; mod task; @@ -15,11 +16,12 @@ use switch::__switch; pub use context::TaskContext; pub use id::{kstack_alloc, pid_alloc, KernelStack, PidHandle}; -pub use manager::add_task; +pub use manager::{add_task, pid2process, remove_from_pid2process}; pub use processor::{ current_kstack_top, current_process, current_task, current_trap_cx, current_trap_cx_user_va, current_user_token, run_tasks, schedule, take_current_task, }; +pub use signal::SignalFlags; pub use task::{TaskControlBlock, TaskStatus}; pub fn suspend_current_and_run_next() { @@ -64,6 +66,7 @@ pub fn exit_current_and_run_next(exit_code: i32) { // however, if this is the main thread of current process // the process should terminate at once if tid == 0 { + remove_from_pid2process(process.getpid()); let mut process_inner = process.inner_exclusive_access(); // mark this process as a zombie process process_inner.is_zombie = true; @@ -111,3 +114,15 @@ lazy_static! { pub fn add_initproc() { let _initproc = INITPROC.clone(); } + +pub fn check_signals_of_current() -> Option<(i32, &'static str)> { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + process_inner.signals.check_error() +} + +pub fn current_add_signal(signal: SignalFlags) { + let process = current_process(); + let mut process_inner = process.inner_exclusive_access(); + process_inner.signals |= signal; +} diff --git a/os/src/task/process.rs b/os/src/task/process.rs index c3935447..ffff14fe 100644 --- a/os/src/task/process.rs +++ b/os/src/task/process.rs @@ -1,6 +1,7 @@ -use super::add_task; use super::id::RecycleAllocator; +use super::manager::insert_into_pid2process; use super::TaskControlBlock; +use super::{add_task, SignalFlags}; use super::{pid_alloc, PidHandle}; use crate::fs::{File, Stdin, Stdout}; use crate::mm::{translated_refmut, MemorySet, KERNEL_SPACE}; @@ -26,6 +27,7 @@ pub struct ProcessControlBlockInner { pub children: Vec>, pub exit_code: i32, pub fd_table: Vec>>, + pub signals: SignalFlags, pub tasks: Vec>>, pub task_res_allocator: RecycleAllocator, pub mutex_list: Vec>>, @@ -92,6 +94,7 @@ impl ProcessControlBlock { // 2 -> stderr Some(Arc::new(Stdout)), ], + signals: SignalFlags::empty(), tasks: Vec::new(), task_res_allocator: RecycleAllocator::new(), mutex_list: Vec::new(), @@ -123,6 +126,7 @@ impl ProcessControlBlock { let mut process_inner = process.inner_exclusive_access(); process_inner.tasks.push(Some(Arc::clone(&task))); drop(process_inner); + insert_into_pid2process(process.getpid(), Arc::clone(&process)); // add main thread to scheduler add_task(task); process @@ -209,6 +213,7 @@ impl ProcessControlBlock { children: Vec::new(), exit_code: 0, fd_table: new_fd_table, + signals: SignalFlags::empty(), tasks: Vec::new(), task_res_allocator: RecycleAllocator::new(), mutex_list: Vec::new(), @@ -242,6 +247,7 @@ impl ProcessControlBlock { let trap_cx = task_inner.get_trap_cx(); trap_cx.kernel_sp = task.kstack.get_top(); drop(task_inner); + insert_into_pid2process(child.getpid(), Arc::clone(&child)); // add this thread to scheduler add_task(task); child diff --git a/os/src/task/signal.rs b/os/src/task/signal.rs new file mode 100644 index 00000000..46f1ad9d --- /dev/null +++ b/os/src/task/signal.rs @@ -0,0 +1,29 @@ +use bitflags::*; + +bitflags! { + pub struct SignalFlags: u32 { + const SIGINT = 1 << 2; + const SIGILL = 1 << 4; + const SIGABRT = 1 << 6; + const SIGFPE = 1 << 8; + const SIGSEGV = 1 << 11; + } +} + +impl SignalFlags { + pub fn check_error(&self) -> Option<(i32, &'static str)> { + if self.contains(Self::SIGINT) { + Some((-2, "Killed, SIGINT=2")) + } else if self.contains(Self::SIGILL) { + Some((-4, "Illegal Instruction, SIGILL=4")) + } else if self.contains(Self::SIGABRT) { + Some((-6, "Aborted, SIGABRT=6")) + } else if self.contains(Self::SIGFPE) { + Some((-8, "Erroneous Arithmetic Operation, SIGFPE=8")) + } else if self.contains(Self::SIGSEGV) { + Some((-11, "Segmentation Fault, SIGSEGV=11")) + } else { + None + } + } +} diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 443f330d..6397811a 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -3,8 +3,8 @@ mod context; use crate::config::TRAMPOLINE; use crate::syscall::syscall; use crate::task::{ - current_trap_cx, current_trap_cx_user_va, current_user_token, exit_current_and_run_next, - suspend_current_and_run_next, + check_signals_of_current, current_add_signal, current_trap_cx, current_trap_cx_user_va, + current_user_token, exit_current_and_run_next, suspend_current_and_run_next, SignalFlags, }; use crate::timer::{check_timer, set_next_trigger}; use core::arch::{asm, global_asm}; @@ -60,19 +60,18 @@ pub fn trap_handler() -> ! { | Trap::Exception(Exception::InstructionPageFault) | Trap::Exception(Exception::LoadFault) | Trap::Exception(Exception::LoadPageFault) => { + /* println!( "[kernel] {:?} in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.", scause.cause(), stval, current_trap_cx().sepc, ); - // page fault exit code - exit_current_and_run_next(-2); + */ + current_add_signal(SignalFlags::SIGSEGV); } Trap::Exception(Exception::IllegalInstruction) => { - println!("[kernel] IllegalInstruction in application, kernel killed it."); - // illegal instruction exit code - exit_current_and_run_next(-3); + current_add_signal(SignalFlags::SIGILL); } Trap::Interrupt(Interrupt::SupervisorTimer) => { set_next_trigger(); @@ -87,6 +86,11 @@ pub fn trap_handler() -> ! { ); } } + // check signals + if let Some((errno, msg)) = check_signals_of_current() { + println!("[kernel] {}", msg); + exit_current_and_run_next(errno); + } trap_return(); } diff --git a/user/Cargo.toml b/user/Cargo.toml index d50a0ba0..f522c9e9 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -8,4 +8,5 @@ edition = "2018" [dependencies] buddy_system_allocator = "0.6" -bitflags = "1.2.1" \ No newline at end of file +bitflags = "1.2.1" +riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } diff --git a/user/src/bin/cat.rs b/user/src/bin/cat.rs index 424f44f3..9cbed233 100644 --- a/user/src/bin/cat.rs +++ b/user/src/bin/cat.rs @@ -15,7 +15,7 @@ pub fn main(argc: usize, argv: &[&str]) -> i32 { panic!("Error occured when opening file"); } let fd = fd as usize; - let mut buf = [0u8; 16]; + let mut buf = [0u8; 256]; loop { let size = read(fd, &mut buf) as usize; if size == 0 { diff --git a/user/src/bin/infloop.rs b/user/src/bin/infloop.rs new file mode 100644 index 00000000..1f248534 --- /dev/null +++ b/user/src/bin/infloop.rs @@ -0,0 +1,10 @@ +#![no_std] +#![no_main] +#![allow(clippy::empty_loop)] + +extern crate user_lib; + +#[no_mangle] +pub fn main(_argc: usize, _argv: &[&str]) -> ! { + loop {} +} diff --git a/user/src/bin/priv_csr.rs b/user/src/bin/priv_csr.rs new file mode 100644 index 00000000..fbd678f7 --- /dev/null +++ b/user/src/bin/priv_csr.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use riscv::register::sstatus::{self, SPP}; + +#[no_mangle] +fn main() -> i32 { + println!("Try to access privileged CSR in U Mode"); + println!("Kernel should kill this application!"); + unsafe { + sstatus::set_spp(SPP::User); + } + 0 +} diff --git a/user/src/bin/priv_inst.rs b/user/src/bin/priv_inst.rs new file mode 100644 index 00000000..04dac37e --- /dev/null +++ b/user/src/bin/priv_inst.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use core::arch::asm; + +#[no_mangle] +fn main() -> i32 { + println!("Try to execute privileged instruction in U Mode"); + println!("Kernel should kill this application!"); + unsafe { + asm!("sret"); + } + 0 +} diff --git a/user/src/bin/store_fault.rs b/user/src/bin/store_fault.rs new file mode 100644 index 00000000..f8023eb7 --- /dev/null +++ b/user/src/bin/store_fault.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +fn main() -> i32 { + println!("Into Test store_fault, we will insert an invalid store operation..."); + println!("Kernel should kill this application!"); + unsafe { + core::ptr::null_mut::().write_volatile(0); + } + 0 +} diff --git a/user/src/bin/until_timeout.rs b/user/src/bin/until_timeout.rs new file mode 100644 index 00000000..a0007cb5 --- /dev/null +++ b/user/src/bin/until_timeout.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, get_time, kill, waitpid, waitpid_nb, SignalFlags}; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + assert_eq!(argc, 3, "argc must be 3!"); + let timeout_ms = argv[2] + .parse::() + .expect("Error when parsing timeout!"); + let pid = fork() as usize; + if pid == 0 { + if exec(argv[1], &[core::ptr::null::()]) != 0 { + println!("Error when executing '{}'", argv[1]); + return -4; + } + } else { + let start_time = get_time(); + let mut child_exited = false; + let mut exit_code: i32 = 0; + loop { + if get_time() - start_time > timeout_ms { + break; + } + if waitpid_nb(pid, &mut exit_code) as usize == pid { + child_exited = true; + println!( + "child exited in {}ms, exit_code = {}", + get_time() - start_time, + exit_code, + ); + } + } + if !child_exited { + println!("child has run for {}ms, kill it!", timeout_ms); + kill(pid, SignalFlags::SIGINT.bits()); + assert_eq!(waitpid(pid, &mut exit_code) as usize, pid); + println!("exit code of the child is {}", exit_code); + } + } + 0 +} diff --git a/user/src/lang_items.rs b/user/src/lang_items.rs index c65aa421..df0467c3 100644 --- a/user/src/lang_items.rs +++ b/user/src/lang_items.rs @@ -1,4 +1,4 @@ -use super::exit; +use super::{getpid, kill, SignalFlags}; #[panic_handler] fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { @@ -13,5 +13,6 @@ fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { } else { println!("Panicked: {}", err); } - exit(-1); + kill(getpid() as usize, SignalFlags::SIGABRT.bits()); + unreachable!() } diff --git a/user/src/lib.rs b/user/src/lib.rs index 50b6439e..60f39ffb 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -127,6 +127,25 @@ pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { } } } + +pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize { + sys_waitpid(pid as isize, exit_code as *mut _) +} + +bitflags! { + pub struct SignalFlags: i32 { + const SIGINT = 1 << 2; + const SIGILL = 1 << 4; + const SIGABRT = 1 << 6; + const SIGFPE = 1 << 8; + const SIGSEGV = 1 << 11; + } +} + +pub fn kill(pid: usize, signal: i32) -> isize { + sys_kill(pid, signal) +} + pub fn sleep(sleep_ms: usize) { sys_sleep(sleep_ms); } diff --git a/user/src/syscall.rs b/user/src/syscall.rs index ce30d004..4cd714ef 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -9,6 +9,7 @@ const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; const SYSCALL_SLEEP: usize = 101; const SYSCALL_YIELD: usize = 124; +const SYSCALL_KILL: usize = 129; const SYSCALL_GET_TIME: usize = 169; const SYSCALL_GETPID: usize = 172; const SYSCALL_FORK: usize = 220; @@ -81,6 +82,10 @@ pub fn sys_yield() -> isize { syscall(SYSCALL_YIELD, [0, 0, 0]) } +pub fn sys_kill(pid: usize, signal: i32) -> isize { + syscall(SYSCALL_KILL, [pid, signal as usize, 0]) +} + pub fn sys_get_time() -> isize { syscall(SYSCALL_GET_TIME, [0, 0, 0]) }