From 92b517bcd472dd5a30f3f78d9d3521f92e27afd3 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 5 Nov 2021 15:24:10 +0200 Subject: [PATCH] feature: waitpid system call --- Cargo.lock | 14 +++++------ Cargo.toml | 2 +- Makefile | 9 ++++--- init/src/main.rs | 26 -------------------- kernel/src/proc/io.rs | 5 ++++ kernel/src/proc/process.rs | 30 ++++++++++++++++++++--- kernel/src/proc/wait.rs | 50 +++++++++++++++++++++++--------------- kernel/src/syscall/mod.rs | 13 ++++++++++ syscall/src/abi.rs | 1 + syscall/src/calls.rs | 15 +++++++++++- {init => user}/Cargo.toml | 10 +++++++- user/src/init/main.rs | 26 ++++++++++++++++++++ user/src/shell/main.rs | 31 +++++++++++++++++++++++ 13 files changed, 169 insertions(+), 63 deletions(-) delete mode 100644 init/src/main.rs rename {init => user}/Cargo.toml (67%) create mode 100644 user/src/init/main.rs create mode 100644 user/src/shell/main.rs diff --git a/Cargo.lock b/Cargo.lock index 5ddad75..1b14465 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,13 +70,6 @@ dependencies = [ "unsafe_unwrap", ] -[[package]] -name = "init" -version = "0.1.0" -dependencies = [ - "libusr", -] - [[package]] name = "kernel" version = "0.1.0" @@ -233,6 +226,13 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80" +[[package]] +name = "user" +version = "0.1.0" +dependencies = [ + "libusr", +] + [[package]] name = "vfs" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 19c0110..183dc28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,6 @@ members = [ "libcommon", "kernel", "libusr", - "init", + "user", "error" ] diff --git a/Makefile b/Makefile index b124ef1..bb400bb 100644 --- a/Makefile +++ b/Makefile @@ -86,13 +86,14 @@ ifeq ($(MACH),orangepi3) endif initrd: - cd init && cargo build \ + cd user && cargo build \ --target=../etc/$(ARCH)-osdev5.json \ -Z build-std=core,alloc,compiler_builtins \ $(CARGO_COMMON_OPTS) - cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O) - echo This is a test file >$(O)/test.txt - cd $(O) && tar cf initrd.img init test.txt + mkdir -p $(O)/rootfs/bin + cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init + cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin + cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"` ifeq ($(MACH),orangepi3) $(MKIMAGE) \ -A arm64 \ diff --git a/init/src/main.rs b/init/src/main.rs deleted file mode 100644 index b315b8d..0000000 --- a/init/src/main.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![feature(asm)] -#![no_std] -#![no_main] - -#[macro_use] -extern crate libusr; - -use libusr::sys::{FileMode, OpenFlags, Stat, AT_EMPTY_PATH, AT_FDCWD}; - -#[no_mangle] -fn main() -> i32 { - let mut stat = Stat::default(); - let fd = unsafe { - libusr::sys::sys_openat( - AT_FDCWD, - "/test.txt", - FileMode::empty(), - OpenFlags::O_RDONLY, - ) - }; - println!("fd = {}", fd); - let ret = unsafe { libusr::sys::sys_fstatat(fd, "", &mut stat, AT_EMPTY_PATH) }; - println!("{}, {:?}", ret, stat); - - -1 -} diff --git a/kernel/src/proc/io.rs b/kernel/src/proc/io.rs index ed37cb8..2267a10 100644 --- a/kernel/src/proc/io.rs +++ b/kernel/src/proc/io.rs @@ -76,6 +76,11 @@ impl ProcessIo { pub(super) fn handle_cloexec(&mut self) { self.files.retain(|_, entry| !entry.borrow().is_cloexec()); } + + pub(super) fn handle_exit(&mut self) { + self.files.clear(); + self.ioctx.take(); + } } impl Default for ProcessIo { diff --git a/kernel/src/proc/process.rs b/kernel/src/proc/process.rs index 027338e..04a0877 100644 --- a/kernel/src/proc/process.rs +++ b/kernel/src/proc/process.rs @@ -5,7 +5,7 @@ use crate::mem::{ phys::{self, PageUsage}, virt::{MapAttributes, Space}, }; -use crate::proc::{ProcessIo, PROCESSES, SCHED}; +use crate::proc::{wait::Wait, ProcessIo, PROCESSES, SCHED}; use crate::sync::IrqSafeSpinLock; use alloc::rc::Rc; use core::cell::UnsafeCell; @@ -54,6 +54,7 @@ struct ProcessInner { pub struct Process { ctx: UnsafeCell, inner: IrqSafeSpinLock, + exit_wait: Wait, /// Process I/O context pub io: IrqSafeSpinLock, } @@ -70,6 +71,12 @@ impl From<()> for ExitCode { } } +impl From for i32 { + fn from(f: ExitCode) -> Self { + f.0 + } +} + impl Pid { /// Kernel idle process always has PID of zero pub const IDLE: Self = Self(Self::KERNEL_BIT); @@ -117,6 +124,10 @@ impl Pid { pub const fn value(self) -> u32 { self.0 } + + pub const unsafe fn from_raw(num: u32) -> Self { + Self(num) + } } impl fmt::Display for Pid { @@ -139,6 +150,10 @@ impl Process { SCHED.current_process() } + pub fn get(pid: Pid) -> Option { + PROCESSES.lock().get(&pid).cloned() + } + /// Schedules an initial thread for execution /// /// # Safety @@ -215,6 +230,7 @@ impl Process { let res = Rc::new(Self { ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)), io: IrqSafeSpinLock::new(ProcessIo::new()), + exit_wait: Wait::new(), inner: IrqSafeSpinLock::new(ProcessInner { id, exit: None, @@ -242,6 +258,7 @@ impl Process { let dst = Rc::new(Self { ctx: UnsafeCell::new(Context::fork(frame, dst_ttbr0)), io: IrqSafeSpinLock::new(src_io.fork()?), + exit_wait: Wait::new(), inner: IrqSafeSpinLock::new(ProcessInner { id: dst_id, exit: None, @@ -267,9 +284,13 @@ impl Process { assert!(lock.exit.is_none()); lock.exit = Some(status); lock.state = State::Finished; + + self.io.lock().handle_exit(); + SCHED.dequeue(lock.id); drop }; + self.exit_wait.wakeup_all(); if drop { SCHED.switch(true); panic!("This code should never run"); @@ -295,12 +316,13 @@ impl Process { .ok_or(Errno::DoesNotExist)?; if let Some(r) = proc.collect() { + // TODO drop the process struct itself PROCESSES.lock().remove(&proc.id()); - debugln!("r = {}", Rc::strong_count(&proc)); + debugln!("pid {} has {} refs", proc.id(), Rc::strong_count(&proc)); return Ok(r); - } else { - cortex_a::asm::wfi(); } + + proc.exit_wait.wait(None)?; } } diff --git a/kernel/src/proc/wait.rs b/kernel/src/proc/wait.rs index 74f42c8..c3f49a0 100644 --- a/kernel/src/proc/wait.rs +++ b/kernel/src/proc/wait.rs @@ -62,31 +62,43 @@ impl Wait { } } + fn wakeup_some(&self, mut limit: usize) -> usize { + // No IRQs will arrive now == safe to manipulate tick list + let mut queue = self.queue.lock(); + let mut count = 0; + while limit != 0 && !queue.is_empty() { + let pid = queue.pop_front(); + if let Some(pid) = pid { + let mut tick_lock = TICK_LIST.lock(); + let mut cursor = tick_lock.cursor_front_mut(); + while let Some(item) = cursor.current() { + if pid == item.pid { + cursor.remove_current(); + break; + } else { + cursor.move_next(); + } + } + drop(tick_lock); + + proc::process(pid).set_wait_flag(false); + SCHED.enqueue(pid); + } + + limit -= 1; + count += 1; + } + count + } + /// Notifies all processes waiting for this event pub fn wakeup_all(&self) { - todo!() + self.wakeup_some(usize::MAX); } /// Notifies a single process waiting for this event pub fn wakeup_one(&self) { - // No IRQs will arrive now == safe to manipulate tick list - let mut tick_lock = TICK_LIST.lock(); - let pid = self.queue.lock().pop_front(); - if let Some(pid) = pid { - let mut cursor = tick_lock.cursor_front_mut(); - while let Some(item) = cursor.current() { - if pid == item.pid { - cursor.remove_current(); - break; - } else { - cursor.move_next(); - } - } - drop(tick_lock); - - proc::process(pid).set_wait_flag(false); - SCHED.enqueue(pid); - } + self.wakeup_some(1); } /// Suspends current process until event is signalled or diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index e9a80d7..24b9c0f 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -122,6 +122,19 @@ pub fn syscall(num: usize, args: &[usize]) -> Result { Process::execve(|space| elf::load_elf(space, file), 0).unwrap(); panic!(); } + abi::SYS_WAITPID => { + // TODO special "pid" values + let pid = unsafe { Pid::from_raw(args[0] as u32) }; + let status = validate_user_ptr_struct::(args[1])?; + + match Process::waitpid(pid) { + Ok(exit) => { + *status = i32::from(exit); + Ok(0) + }, + _ => todo!() + } + } // Extra system calls abi::SYS_EX_DEBUG_TRACE => { diff --git a/syscall/src/abi.rs b/syscall/src/abi.rs index c4a9c70..f27b816 100644 --- a/syscall/src/abi.rs +++ b/syscall/src/abi.rs @@ -9,3 +9,4 @@ pub const SYS_FSTATAT: usize = 5; pub const SYS_CLOSE: usize = 6; pub const SYS_FORK: usize = 7; pub const SYS_EXECVE: usize = 8; +pub const SYS_WAITPID: usize = 9; diff --git a/syscall/src/calls.rs b/syscall/src/calls.rs index 7494c3e..e52df4b 100644 --- a/syscall/src/calls.rs +++ b/syscall/src/calls.rs @@ -55,7 +55,7 @@ macro_rules! argn { /// Pointer/base argument macro_rules! argp { ($a:expr) => { - $a as *mut core::ffi::c_void as usize + $a as usize }; } // /// Immutable pointer/base argument @@ -175,3 +175,16 @@ pub unsafe fn sys_execve(pathname: &str) -> i32 { argn!(pathname.len()) ) as i32 } + +/// # Safety +/// +/// System call +#[inline(always)] +pub unsafe fn sys_waitpid(pid: u32, status: &mut i32) -> i32 { + syscall!( + abi::SYS_WAITPID, + argn!(pid), + argp!(status as *mut i32) + ) as i32 +} + diff --git a/init/Cargo.toml b/user/Cargo.toml similarity index 67% rename from init/Cargo.toml rename to user/Cargo.toml index a687bfa..2b8da15 100644 --- a/init/Cargo.toml +++ b/user/Cargo.toml @@ -1,9 +1,17 @@ [package] -name = "init" +name = "user" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "init" +path = "src/init/main.rs" + +[[bin]] +name = "shell" +path = "src/shell/main.rs" + [dependencies] libusr = { path = "../libusr" } diff --git a/user/src/init/main.rs b/user/src/init/main.rs new file mode 100644 index 0000000..d3d922d --- /dev/null +++ b/user/src/init/main.rs @@ -0,0 +1,26 @@ +#![feature(asm)] +#![no_std] +#![no_main] + +#[macro_use] +extern crate libusr; + +#[no_mangle] +fn main() -> i32 { + let pid = unsafe { libusr::sys::sys_fork() }; + if pid < 0 { + eprintln!("fork() failed"); + return -1; + } else if pid == 0 { + return unsafe { libusr::sys::sys_execve("/bin/shell") }; + } else { + let mut status = 0; + let res = unsafe { libusr::sys::sys_waitpid(pid as u32, &mut status) }; + if res > 0 { + println!("Process {} exited with status {}", pid, status); + } else { + eprintln!("waitpid() failed"); + } + } + loop {} +} diff --git a/user/src/shell/main.rs b/user/src/shell/main.rs new file mode 100644 index 0000000..87c444b --- /dev/null +++ b/user/src/shell/main.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate libusr; + +fn readline(fd: i32, buf: &mut [u8]) -> Result<&str, ()> { + let count = unsafe { libusr::sys::sys_read(fd, buf) }; + if count >= 0 { + core::str::from_utf8(&buf[..count as usize]).map_err(|_| ()) + } else { + Err(()) + } +} + +#[no_mangle] +fn main() -> i32 { + let mut buf = [0; 512]; + loop { + print!("> "); + let line = readline(libusr::sys::STDIN_FILENO, &mut buf).unwrap(); + + println!(":: {:?}", line); + + if line == "quit" || line == "exit" { + break; + } + } + + return 0; +}