From 2811808a736851901ff838df8d484c3cd8d082d4 Mon Sep 17 00:00:00 2001 From: Yu Chen Date: Tue, 28 Jun 2022 09:25:52 +0800 Subject: [PATCH] add test apps --- user/.cargo/config | 7 + user/.gitignore | 4 + user/Cargo.toml | 19 ++ user/Makefile | 64 ++++ user/build.py | 23 ++ user/src/bin/ch2b_bad_address.rs | 16 + user/src/bin/ch2b_bad_instructions.rs | 15 + user/src/bin/ch2b_bad_register.rs | 16 + user/src/bin/ch2b_hello_world.rs | 14 + user/src/bin/ch2b_power_3.rs | 28 ++ user/src/bin/ch2b_power_5.rs | 28 ++ user/src/bin/ch2b_power_7.rs | 28 ++ user/src/bin/ch3_taskinfo.rs | 46 +++ user/src/bin/ch3b_sleep.rs | 24 ++ user/src/bin/ch3b_sleep1.rs | 22 ++ user/src/bin/ch3b_yield0.rs | 30 ++ user/src/bin/ch3b_yield1.rs | 30 ++ user/src/bin/ch3b_yield2.rs | 30 ++ user/src/bin/ch4_mmap0.rs | 33 ++ user/src/bin/ch4_mmap1.rs | 25 ++ user/src/bin/ch4_mmap2.rs | 26 ++ user/src/bin/ch4_mmap3.rs | 25 ++ user/src/bin/ch4_unmap.rs | 36 +++ user/src/bin/ch4_unmap2.rs | 23 ++ user/src/bin/ch5_exit0.rs | 17 + user/src/bin/ch5_exit1.rs | 17 + user/src/bin/ch5_getpid.rs | 18 ++ user/src/bin/ch5_setprio.rs | 20 ++ user/src/bin/ch5_spawn0.rs | 29 ++ user/src/bin/ch5_spawn1.rs | 35 ++ user/src/bin/ch5_stride.rs | 33 ++ user/src/bin/ch5_stride0.rs | 43 +++ user/src/bin/ch5_stride1.rs | 39 +++ user/src/bin/ch5_stride2.rs | 39 +++ user/src/bin/ch5_stride3.rs | 39 +++ user/src/bin/ch5_stride4.rs | 39 +++ user/src/bin/ch5_stride5.rs | 39 +++ user/src/bin/ch5_usertest.rs | 61 ++++ user/src/bin/ch5b_exit.rs | 30 ++ user/src/bin/ch5b_forktest.rs | 34 ++ user/src/bin/ch5b_forktest2.rs | 34 ++ user/src/bin/ch5b_forktest_simple.rs | 28 ++ user/src/bin/ch5b_forktree.rs | 37 +++ user/src/bin/ch5b_initproc.rs | 28 ++ user/src/bin/ch5b_user_shell.rs | 66 ++++ user/src/bin/ch6_file0.rs | 31 ++ user/src/bin/ch6_file1.rs | 26 ++ user/src/bin/ch6_file2.rs | 46 +++ user/src/bin/ch6_file3.rs | 31 ++ user/src/bin/ch6_usertest.rs | 51 +++ user/src/bin/ch6b_cat.rs | 33 ++ user/src/bin/ch6b_filetest_simple.rs | 38 +++ user/src/bin/ch6b_initproc.rs | 28 ++ user/src/bin/ch6b_user_shell.rs | 66 ++++ user/src/bin/ch7_usertest.rs | 29 ++ user/src/bin/ch7b_cat.rs | 34 ++ user/src/bin/ch7b_initproc.rs | 28 ++ user/src/bin/ch7b_pipe_large_test.rs | 71 ++++ user/src/bin/ch7b_pipetest.rs | 44 +++ user/src/bin/ch7b_run_pipe_test.rs | 21 ++ user/src/bin/ch7b_user_shell.rs | 130 ++++++++ user/src/bin/ch7b_yield.rs | 17 + user/src/bin/ch8_deadlock_mutex1.rs | 22 ++ user/src/bin/ch8_deadlock_sem1.rs | 102 ++++++ user/src/bin/ch8_deadlock_sem2.rs | 76 +++++ user/src/bin/ch8_usertest.rs | 60 ++++ user/src/bin/ch8b_initproc.rs | 28 ++ user/src/bin/ch8b_mpsc_sem.rs | 73 +++++ user/src/bin/ch8b_phil_din_mutex.rs | 109 +++++++ user/src/bin/ch8b_race_adder.rs | 43 +++ user/src/bin/ch8b_race_adder_atomic.rs | 52 +++ user/src/bin/ch8b_race_adder_loop.rs | 52 +++ user/src/bin/ch8b_race_adder_mutex_spin.rs | 47 +++ user/src/bin/ch8b_stackful_coroutine.rs | 346 ++++++++++++++++++++ user/src/bin/ch8b_stackless_coroutine.rs | 129 ++++++++ user/src/bin/ch8b_sync_sem.rs | 45 +++ user/src/bin/ch8b_test_condvar.rs | 59 ++++ user/src/bin/ch8b_threads.rs | 62 ++++ user/src/bin/ch8b_threads_arg.rs | 45 +++ user/src/bin/ch8b_user_shell.rs | 214 ++++++++++++ user/src/console.rs | 75 +++++ user/src/lang_items.rs | 17 + user/src/lib.rs | 359 +++++++++++++++++++++ user/src/linker.ld | 33 ++ user/src/syscall.rs | 258 +++++++++++++++ 85 files changed, 4367 insertions(+) create mode 100644 user/.cargo/config create mode 100644 user/.gitignore create mode 100644 user/Cargo.toml create mode 100644 user/Makefile create mode 100644 user/build.py create mode 100644 user/src/bin/ch2b_bad_address.rs create mode 100644 user/src/bin/ch2b_bad_instructions.rs create mode 100644 user/src/bin/ch2b_bad_register.rs create mode 100644 user/src/bin/ch2b_hello_world.rs create mode 100644 user/src/bin/ch2b_power_3.rs create mode 100644 user/src/bin/ch2b_power_5.rs create mode 100644 user/src/bin/ch2b_power_7.rs create mode 100644 user/src/bin/ch3_taskinfo.rs create mode 100644 user/src/bin/ch3b_sleep.rs create mode 100644 user/src/bin/ch3b_sleep1.rs create mode 100644 user/src/bin/ch3b_yield0.rs create mode 100644 user/src/bin/ch3b_yield1.rs create mode 100644 user/src/bin/ch3b_yield2.rs create mode 100644 user/src/bin/ch4_mmap0.rs create mode 100644 user/src/bin/ch4_mmap1.rs create mode 100644 user/src/bin/ch4_mmap2.rs create mode 100644 user/src/bin/ch4_mmap3.rs create mode 100644 user/src/bin/ch4_unmap.rs create mode 100644 user/src/bin/ch4_unmap2.rs create mode 100644 user/src/bin/ch5_exit0.rs create mode 100644 user/src/bin/ch5_exit1.rs create mode 100644 user/src/bin/ch5_getpid.rs create mode 100644 user/src/bin/ch5_setprio.rs create mode 100644 user/src/bin/ch5_spawn0.rs create mode 100644 user/src/bin/ch5_spawn1.rs create mode 100644 user/src/bin/ch5_stride.rs create mode 100644 user/src/bin/ch5_stride0.rs create mode 100644 user/src/bin/ch5_stride1.rs create mode 100644 user/src/bin/ch5_stride2.rs create mode 100644 user/src/bin/ch5_stride3.rs create mode 100644 user/src/bin/ch5_stride4.rs create mode 100644 user/src/bin/ch5_stride5.rs create mode 100644 user/src/bin/ch5_usertest.rs create mode 100644 user/src/bin/ch5b_exit.rs create mode 100644 user/src/bin/ch5b_forktest.rs create mode 100644 user/src/bin/ch5b_forktest2.rs create mode 100644 user/src/bin/ch5b_forktest_simple.rs create mode 100644 user/src/bin/ch5b_forktree.rs create mode 100644 user/src/bin/ch5b_initproc.rs create mode 100644 user/src/bin/ch5b_user_shell.rs create mode 100644 user/src/bin/ch6_file0.rs create mode 100644 user/src/bin/ch6_file1.rs create mode 100644 user/src/bin/ch6_file2.rs create mode 100644 user/src/bin/ch6_file3.rs create mode 100644 user/src/bin/ch6_usertest.rs create mode 100644 user/src/bin/ch6b_cat.rs create mode 100644 user/src/bin/ch6b_filetest_simple.rs create mode 100644 user/src/bin/ch6b_initproc.rs create mode 100644 user/src/bin/ch6b_user_shell.rs create mode 100644 user/src/bin/ch7_usertest.rs create mode 100644 user/src/bin/ch7b_cat.rs create mode 100644 user/src/bin/ch7b_initproc.rs create mode 100644 user/src/bin/ch7b_pipe_large_test.rs create mode 100644 user/src/bin/ch7b_pipetest.rs create mode 100644 user/src/bin/ch7b_run_pipe_test.rs create mode 100644 user/src/bin/ch7b_user_shell.rs create mode 100644 user/src/bin/ch7b_yield.rs create mode 100644 user/src/bin/ch8_deadlock_mutex1.rs create mode 100644 user/src/bin/ch8_deadlock_sem1.rs create mode 100644 user/src/bin/ch8_deadlock_sem2.rs create mode 100644 user/src/bin/ch8_usertest.rs create mode 100644 user/src/bin/ch8b_initproc.rs create mode 100644 user/src/bin/ch8b_mpsc_sem.rs create mode 100644 user/src/bin/ch8b_phil_din_mutex.rs create mode 100644 user/src/bin/ch8b_race_adder.rs create mode 100644 user/src/bin/ch8b_race_adder_atomic.rs create mode 100644 user/src/bin/ch8b_race_adder_loop.rs create mode 100644 user/src/bin/ch8b_race_adder_mutex_spin.rs create mode 100644 user/src/bin/ch8b_stackful_coroutine.rs create mode 100644 user/src/bin/ch8b_stackless_coroutine.rs create mode 100644 user/src/bin/ch8b_sync_sem.rs create mode 100644 user/src/bin/ch8b_test_condvar.rs create mode 100644 user/src/bin/ch8b_threads.rs create mode 100644 user/src/bin/ch8b_threads_arg.rs create mode 100644 user/src/bin/ch8b_user_shell.rs create mode 100644 user/src/console.rs create mode 100644 user/src/lang_items.rs create mode 100644 user/src/lib.rs create mode 100644 user/src/linker.ld create mode 100644 user/src/syscall.rs diff --git a/user/.cargo/config b/user/.cargo/config new file mode 100644 index 0000000..e5ded8a --- /dev/null +++ b/user/.cargo/config @@ -0,0 +1,7 @@ +[build] +target = "riscv64gc-unknown-none-elf" + +[target.riscv64gc-unknown-none-elf] +rustflags = [ + "-Clink-args=-Tsrc/linker.ld", +] diff --git a/user/.gitignore b/user/.gitignore new file mode 100644 index 0000000..b5004eb --- /dev/null +++ b/user/.gitignore @@ -0,0 +1,4 @@ +target/* +.vscode/ +build/ +.idea/ \ No newline at end of file diff --git a/user/Cargo.toml b/user/Cargo.toml new file mode 100644 index 0000000..bdd5019 --- /dev/null +++ b/user/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "user_lib" +version = "0.1.0" +authors = ["Yifan Wu "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +buddy_system_allocator = "0.6" +bitflags = "1.2.1" +spin = "0.9" +lock_api = "=0.4.6" +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } + +[profile.release] +opt-level = "z" # Optimize for size. +strip = true # Automatically strip symbols from the binary. +lto = true diff --git a/user/Makefile b/user/Makefile new file mode 100644 index 0000000..1c87fe9 --- /dev/null +++ b/user/Makefile @@ -0,0 +1,64 @@ +TARGET := riscv64gc-unknown-none-elf +MODE := release +APP_DIR := src/bin +TARGET_DIR := target/$(TARGET)/$(MODE) +BUILD_DIR := build +OBJDUMP := rust-objdump --arch-name=riscv64 +OBJCOPY := rust-objcopy --binary-architecture=riscv64 +PY := python3 + +BASE ?= 0 +CHAPTER ?= 0 +TEST ?= $(CHAPTER) + +ifeq ($(TEST), 0) # No test, deprecated, previously used in v3 + APPS := $(filter-out $(wildcard $(APP_DIR)/ch*.rs), $(wildcard $(APP_DIR)/*.rs)) +else ifeq ($(TEST), 1) # All test + APPS := $(wildcard $(APP_DIR)/ch*.rs) +else + TESTS := $(shell seq $(BASE) $(TEST)) + ifeq ($(BASE), 0) # Normal tests only + APPS := $(foreach T, $(TESTS), $(wildcard $(APP_DIR)/ch$(T)_*.rs)) + else ifeq ($(BASE), 1) # Basic tests only + APPS := $(foreach T, $(TESTS), $(wildcard $(APP_DIR)/ch$(T)b_*.rs)) + else # Basic and normal + APPS := $(foreach T, $(TESTS), $(wildcard $(APP_DIR)/ch$(T)*.rs)) + endif +endif + +ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS)) + +binary: + @echo $(ELFS) + @if [ ${CHAPTER} -gt 3 ]; then \ + cargo build --release ;\ + else \ + CHAPTER=$(CHAPTER) python3 build.py ;\ + fi + @$(foreach elf, $(ELFS), \ + $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf)); \ + cp $(elf) $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.elf, $(elf));) + +disasm: + @$(foreach elf, $(ELFS), \ + $(OBJDUMP) $(elf) -S > $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.asm, $(elf));) + @$(foreach t, $(ELFS), cp $(t).asm $(BUILD_DIR)/asm/;) + +pre: + @mkdir -p $(BUILD_DIR)/bin/ + @mkdir -p $(BUILD_DIR)/elf/ + @mkdir -p $(BUILD_DIR)/app/ + @mkdir -p $(BUILD_DIR)/asm/ + @$(foreach t, $(APPS), cp $(t) $(BUILD_DIR)/app/;) + +build: clean pre binary + @$(foreach t, $(ELFS), cp $(t).bin $(BUILD_DIR)/bin/;) + @$(foreach t, $(ELFS), cp $(t).elf $(BUILD_DIR)/elf/;) + +clean: + @cargo clean + @rm -rf $(BUILD_DIR) + +all: build + +.PHONY: elf binary build clean all \ No newline at end of file diff --git a/user/build.py b/user/build.py new file mode 100644 index 0000000..de0cb66 --- /dev/null +++ b/user/build.py @@ -0,0 +1,23 @@ +import os + +base_address = 0x80400000 +step = 0x20000 +linker = "src/linker.ld" + +app_id = 0 +apps = os.listdir("build/app") +apps.sort() +chapter = os.getenv("CHAPTER") + +for app in apps: + app = app[: app.find(".")] + os.system( + "cargo rustc --bin %s --release -- -Clink-args=-Ttext=%x" + % (app, base_address + step * app_id) + ) + print( + "[build.py] application %s start with address %s" + % (app, hex(base_address + step * app_id)) + ) + if chapter == '3': + app_id = app_id + 1 diff --git a/user/src/bin/ch2b_bad_address.rs b/user/src/bin/ch2b_bad_address.rs new file mode 100644 index 0000000..bb6dfeb --- /dev/null +++ b/user/src/bin/ch2b_bad_address.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +/// 由于 rustsbi 的问题,该程序无法正确退出 +/// > rustsbi 0.2.0-alpha.1 已经修复,可以正常退出 + +#[no_mangle] +pub fn main() -> isize { + unsafe { + #[allow(clippy::zero_ptr)] + (0x0 as *mut u8).write_volatile(0); + } + panic!("FAIL: T.T\n"); +} \ No newline at end of file diff --git a/user/src/bin/ch2b_bad_instructions.rs b/user/src/bin/ch2b_bad_instructions.rs new file mode 100644 index 0000000..f60791e --- /dev/null +++ b/user/src/bin/ch2b_bad_instructions.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +/// 由于 rustsbi 的问题,该程序无法正确退出 +/// > rustsbi 0.2.0-alpha.1 已经修复,可以正常退出 + +#[no_mangle] +pub fn main() -> ! { + unsafe { + core::arch::asm!("sret"); + } + panic!("FAIL: T.T\n"); +} \ No newline at end of file diff --git a/user/src/bin/ch2b_bad_register.rs b/user/src/bin/ch2b_bad_register.rs new file mode 100644 index 0000000..0bed325 --- /dev/null +++ b/user/src/bin/ch2b_bad_register.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +/// 由于 rustsbi 的问题,该程序无法正确退出 +/// > rustsbi 0.2.0-alpha.1 已经修复,可以正常退出 + +#[no_mangle] +pub fn main() -> ! { + let mut sstatus: usize; + unsafe { + core::arch::asm!("csrr {}, sstatus", out(reg) sstatus); + } + panic!("(-_-) I get sstatus:{:x}\nFAIL: T.T\n", sstatus); +} diff --git a/user/src/bin/ch2b_hello_world.rs b/user/src/bin/ch2b_hello_world.rs new file mode 100644 index 0000000..327dec7 --- /dev/null +++ b/user/src/bin/ch2b_hello_world.rs @@ -0,0 +1,14 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +/// 正确输出: +/// Hello world from user mode program! + +#[no_mangle] +fn main() -> i32 { + println!("Hello, world from user mode program!"); + 0 +} \ No newline at end of file diff --git a/user/src/bin/ch2b_power_3.rs b/user/src/bin/ch2b_power_3.rs new file mode 100644 index 0000000..e0ec5f3 --- /dev/null +++ b/user/src/bin/ch2b_power_3.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +const LEN: usize = 100; + +#[no_mangle] +fn main() -> i32 { + let p = 3u64; + let m = 998244353u64; + let iter: usize = 200000; + let mut s = [0u64; LEN]; + let mut cur = 0usize; + s[cur] = 1; + for i in 1..=iter { + let next = if cur + 1 == LEN { 0 } else { cur + 1 }; + s[next] = s[cur] * p % m; + cur = next; + if i % 10000 == 0 { + println!("power_3 [{}/{}]", i, iter); + } + } + println!("{}^{} = {}(MOD {})", p, iter, s[cur], m); + println!("Test power_3 OK!"); + 0 +} \ No newline at end of file diff --git a/user/src/bin/ch2b_power_5.rs b/user/src/bin/ch2b_power_5.rs new file mode 100644 index 0000000..e47761b --- /dev/null +++ b/user/src/bin/ch2b_power_5.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +const LEN: usize = 100; + +#[no_mangle] +fn main() -> i32 { + let p = 5u64; + let m = 998244353u64; + let iter: usize = 140000; + let mut s = [0u64; LEN]; + let mut cur = 0usize; + s[cur] = 1; + for i in 1..=iter { + let next = if cur + 1 == LEN { 0 } else { cur + 1 }; + s[next] = s[cur] * p % m; + cur = next; + if i % 10000 == 0 { + println!("power_5 [{}/{}]", i, iter); + } + } + println!("{}^{} = {}(MOD {})", p, iter, s[cur], m); + println!("Test power_5 OK!"); + 0 +} diff --git a/user/src/bin/ch2b_power_7.rs b/user/src/bin/ch2b_power_7.rs new file mode 100644 index 0000000..a97d2f5 --- /dev/null +++ b/user/src/bin/ch2b_power_7.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +const LEN: usize = 100; + +#[no_mangle] +fn main() -> i32 { + let p = 7u64; + let m = 998244353u64; + let iter: usize = 160000; + let mut s = [0u64; LEN]; + let mut cur = 0usize; + s[cur] = 1; + for i in 1..=iter { + let next = if cur + 1 == LEN { 0 } else { cur + 1 }; + s[next] = s[cur] * p % m; + cur = next; + if i % 10000 == 0 { + println!("power_7 [{}/{}]", i, iter); + } + } + println!("{}^{} = {}(MOD {})", p, iter, s[cur], m); + println!("Test power_7 OK!"); + 0 +} diff --git a/user/src/bin/ch3_taskinfo.rs b/user/src/bin/ch3_taskinfo.rs new file mode 100644 index 0000000..7f55f15 --- /dev/null +++ b/user/src/bin/ch3_taskinfo.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +use user_lib::{ + get_time, println, sleep, task_info, TaskInfo, TaskStatus, SYSCALL_EXIT, SYSCALL_GETTIMEOFDAY, + SYSCALL_TASK_INFO, SYSCALL_WRITE, SYSCALL_YIELD, +}; + +#[no_mangle] +pub fn main() -> usize { + let t1 = get_time() as usize; + let info = TaskInfo::new(); + get_time(); + sleep(500); + let t2 = get_time() as usize; + // 注意本次 task info 调用也计入 + assert_eq!(0, task_info(&info)); + let t3 = get_time() as usize; + assert!(3 <= info.syscall_times[SYSCALL_GETTIMEOFDAY]); + assert_eq!(1, info.syscall_times[SYSCALL_TASK_INFO]); + assert_eq!(0, info.syscall_times[SYSCALL_WRITE]); + assert!(0 < info.syscall_times[SYSCALL_YIELD]); + assert_eq!(0, info.syscall_times[SYSCALL_EXIT]); + assert!(t2 - t1 <= info.time + 1); + assert!(info.time < t3 - t1 + 100); + assert!(info.status == TaskStatus::Running); + + // 想想为什么 write 调用是两次 + println!("string from task info test\n"); + let t4 = get_time() as usize; + assert_eq!(0, task_info(&info)); + let t5 = get_time() as usize; + assert!(5 <= info.syscall_times[SYSCALL_GETTIMEOFDAY]); + assert_eq!(2, info.syscall_times[SYSCALL_TASK_INFO]); + assert_eq!(2, info.syscall_times[SYSCALL_WRITE]); + assert!(0 < info.syscall_times[SYSCALL_YIELD]); + assert_eq!(0, info.syscall_times[SYSCALL_EXIT]); + assert!(t4 - t1 <= info.time + 1); + assert!(info.time < t5 - t1 + 100); + assert!(info.status == TaskStatus::Running); + + println!("Test task info OK!"); + 0 +} diff --git a/user/src/bin/ch3b_sleep.rs b/user/src/bin/ch3b_sleep.rs new file mode 100644 index 0000000..1312f6c --- /dev/null +++ b/user/src/bin/ch3b_sleep.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{get_time, yield_}; + +/// 正确输出:(无报错信息) +/// get_time OK! {...} +/// Test sleep OK! + +#[no_mangle] +fn main() -> i32 { + let current_time = get_time(); + assert!(current_time > 0); + println!("get_time OK! {}", current_time); + let wait_for = current_time + 3000; + while get_time() < wait_for { + yield_(); + } + println!("Test sleep OK!"); + 0 +} diff --git a/user/src/bin/ch3b_sleep1.rs b/user/src/bin/ch3b_sleep1.rs new file mode 100644 index 0000000..da0cae3 --- /dev/null +++ b/user/src/bin/ch3b_sleep1.rs @@ -0,0 +1,22 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{get_time, sleep}; + +#[no_mangle] +pub fn main() -> i32 { + let start = get_time(); + println!("current time_msec = {}", start); + sleep(100); + let end = get_time(); + println!( + "time_msec = {} after sleeping 100 ticks, delta = {}ms!", + end, + end - start + ); + println!("Test sleep1 passed!"); + 0 +} diff --git a/user/src/bin/ch3b_yield0.rs b/user/src/bin/ch3b_yield0.rs new file mode 100644 index 0000000..1e09edd --- /dev/null +++ b/user/src/bin/ch3b_yield0.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::yield_; + +const WIDTH: usize = 10; +const HEIGHT: usize = 5; + +/* +理想结果:三个程序交替输出 ABC +*/ + +#[no_mangle] +fn main() -> i32 { + for i in 0..HEIGHT { + let buf = ['A' as u8; WIDTH]; + println!( + "{} [{}/{}]", + core::str::from_utf8(&buf).unwrap(), + i + 1, + HEIGHT + ); + yield_(); + } + println!("Test write A OK!"); + 0 +} diff --git a/user/src/bin/ch3b_yield1.rs b/user/src/bin/ch3b_yield1.rs new file mode 100644 index 0000000..ebfc13f --- /dev/null +++ b/user/src/bin/ch3b_yield1.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::yield_; + +const WIDTH: usize = 10; +const HEIGHT: usize = 5; + +/* +理想结果:三个程序交替输出 ABC +*/ + +#[no_mangle] +fn main() -> i32 { + for i in 0..HEIGHT { + let buf = ['B' as u8; WIDTH]; + println!( + "{} [{}/{}]", + core::str::from_utf8(&buf).unwrap(), + i + 1, + HEIGHT + ); + yield_(); + } + println!("Test write B OK!"); + 0 +} diff --git a/user/src/bin/ch3b_yield2.rs b/user/src/bin/ch3b_yield2.rs new file mode 100644 index 0000000..acbaac3 --- /dev/null +++ b/user/src/bin/ch3b_yield2.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::yield_; + +/* +理想结果:三个程序交替输出 ABC +*/ + +const WIDTH: usize = 10; +const HEIGHT: usize = 5; + +#[no_mangle] +fn main() -> i32 { + for i in 0..HEIGHT { + let buf = ['C' as u8; WIDTH]; + println!( + "{} [{}/{}]", + core::str::from_utf8(&buf).unwrap(), + i + 1, + HEIGHT + ); + yield_(); + } + println!("Test write C OK!"); + 0 +} diff --git a/user/src/bin/ch4_mmap0.rs b/user/src/bin/ch4_mmap0.rs new file mode 100644 index 0000000..5247eec --- /dev/null +++ b/user/src/bin/ch4_mmap0.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::mmap; + +/* +理想结果:输出 Test 04_1 OK! +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 3; + assert_eq!(0, mmap(start, len, prot)); + for i in start..(start + len) { + let addr: *mut u8 = i as *mut u8; + unsafe { + *addr = i as u8; + } + } + for i in start..(start + len) { + let addr: *mut u8 = i as *mut u8; + unsafe { + assert_eq!(*addr, i as u8); + } + } + println!("Test 04_1 OK!"); + 0 +} diff --git a/user/src/bin/ch4_mmap1.rs b/user/src/bin/ch4_mmap1.rs new file mode 100644 index 0000000..63d6411 --- /dev/null +++ b/user/src/bin/ch4_mmap1.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::mmap; + +/* +理想结果:程序触发访存异常,被杀死。不输出 error 就算过。 +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 1; + assert_eq!(0, mmap(start, len, prot)); + let addr: *mut u8 = start as *mut u8; + unsafe { + *addr = start as u8; + } + println!("Should cause error, Test 04_2 fail!"); + 0 +} diff --git a/user/src/bin/ch4_mmap2.rs b/user/src/bin/ch4_mmap2.rs new file mode 100644 index 0000000..2489fa6 --- /dev/null +++ b/user/src/bin/ch4_mmap2.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::mmap; + +/* +理想结果:程序触发访存异常,被杀死。不输出 error 就算过。 +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 2; + assert_eq!(0, mmap(start, len, prot)); + let addr: *mut u8 = start as *mut u8; + unsafe { + // *addr = start as u8; // can't write, R == 0 && W == 1 is illegal in riscv + assert!(*addr != 0); + } + println!("Should cause error, Test 04_2 fail!"); + 0 +} diff --git a/user/src/bin/ch4_mmap3.rs b/user/src/bin/ch4_mmap3.rs new file mode 100644 index 0000000..d9d04b6 --- /dev/null +++ b/user/src/bin/ch4_mmap3.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::mmap; + +/* +理想结果:对于错误的 mmap 返回 -1,最终输出 Test 04_4 test OK! +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 3; + assert_eq!(0, mmap(start, len, prot)); + assert_eq!(mmap(start - len, len + 1, prot), -1); + assert_eq!(mmap(start + len + 1, len, prot), -1); + assert_eq!(mmap(start + len, len, 0), -1); + assert_eq!(mmap(start + len, len, prot | 8), -1); + println!("Test 04_4 test OK!"); + 0 +} diff --git a/user/src/bin/ch4_unmap.rs b/user/src/bin/ch4_unmap.rs new file mode 100644 index 0000000..b64f862 --- /dev/null +++ b/user/src/bin/ch4_unmap.rs @@ -0,0 +1,36 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{mmap, munmap}; + +/* +理想结果:输出 Test 04_5 ummap OK! +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 3; + assert_eq!(0, mmap(start, len, prot)); + assert_eq!(mmap(start + len, len * 2, prot), 0); + assert_eq!(munmap(start, len), 0); + assert_eq!(mmap(start - len, len + 1, prot), 0); + for i in (start - len)..(start + len * 3) { + let addr: *mut u8 = i as *mut u8; + unsafe { + *addr = i as u8; + } + } + for i in (start - len)..(start + len * 3) { + let addr: *mut u8 = i as *mut u8; + unsafe { + assert_eq!(*addr, i as u8); + } + } + println!("Test 04_5 ummap OK!"); + 0 +} diff --git a/user/src/bin/ch4_unmap2.rs b/user/src/bin/ch4_unmap2.rs new file mode 100644 index 0000000..85ddb75 --- /dev/null +++ b/user/src/bin/ch4_unmap2.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{mmap, munmap}; + +/* +理想结果:输出 Test 04_6 ummap2 OK! +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 3; + assert_eq!(0, mmap(start, len, prot)); + assert_eq!(munmap(start, len + 1), -1); + assert_eq!(munmap(start + 1, len - 1), -1); + println!("Test 04_6 ummap2 OK!"); + 0 +} diff --git a/user/src/bin/ch5_exit0.rs b/user/src/bin/ch5_exit0.rs new file mode 100644 index 0000000..3bae851 --- /dev/null +++ b/user/src/bin/ch5_exit0.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +extern crate user_lib; +use user_lib::exit; + +/* +辅助测例,正常退出,不输出 FAIL 即可。 +*/ + +#[allow(unreachable_code)] +#[no_mangle] +pub fn main() -> i32 { + exit(66778); + panic!("FAIL: T.T\n"); + 0 +} diff --git a/user/src/bin/ch5_exit1.rs b/user/src/bin/ch5_exit1.rs new file mode 100644 index 0000000..47c09cb --- /dev/null +++ b/user/src/bin/ch5_exit1.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +extern crate user_lib; +use user_lib::exit; + +/* +辅助测例,正常退出,不输出 FAIL 即可。 +*/ + +#[allow(unreachable_code)] +#[no_mangle] +pub fn main() -> i32 { + exit(-233); + panic!("FAIL: T.T\n"); + 0 +} diff --git a/user/src/bin/ch5_getpid.rs b/user/src/bin/ch5_getpid.rs new file mode 100644 index 0000000..66ee732 --- /dev/null +++ b/user/src/bin/ch5_getpid.rs @@ -0,0 +1,18 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::getpid; + +/* +辅助测例 打印子进程 pid +*/ + +#[no_mangle] +pub fn main() -> i32 { + let pid = getpid(); + println!("Test getpid OK! pid = {}", pid); + 0 +} diff --git a/user/src/bin/ch5_setprio.rs b/user/src/bin/ch5_setprio.rs new file mode 100644 index 0000000..6aa1064 --- /dev/null +++ b/user/src/bin/ch5_setprio.rs @@ -0,0 +1,20 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::set_priority; + +/// 正确输出:(无报错信息) +/// Test set_priority OK! + +#[no_mangle] +pub fn main() -> i32 { + assert_eq!(set_priority(10), 10); + assert_eq!(set_priority(isize::MAX), isize::MAX); + assert_eq!(set_priority(0), -1); + assert_eq!(set_priority(1), -1); + assert_eq!(set_priority(-10), -1); + println!("Test set_priority OK!"); + 0 +} diff --git a/user/src/bin/ch5_spawn0.rs b/user/src/bin/ch5_spawn0.rs new file mode 100644 index 0000000..aff47ac --- /dev/null +++ b/user/src/bin/ch5_spawn0.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{spawn, wait}; +const MAX_CHILD: usize = 40; + +/* +理想结果:生成 MAX_CHILD 个 getpid 的子进程,全部结束后,输出 Test spawn0 OK! +*/ + +#[no_mangle] +pub fn main() -> i32 { + for _ in 0..MAX_CHILD { + let cpid = spawn("ch5_getpid\0"); + assert!(cpid >= 0, "child pid invalid"); + println!("new child {}", cpid); + } + let mut exit_code: i32 = 0; + for _ in 0..MAX_CHILD { + assert!(wait(&mut exit_code) > 0, "wait stopped early"); + assert_eq!(exit_code, 0, "error exit ocde {}", exit_code); + } + assert!(wait(&mut exit_code) <= 0, "wait got too many"); + println!("Test spawn0 OK!"); + 0 +} diff --git a/user/src/bin/ch5_spawn1.rs b/user/src/bin/ch5_spawn1.rs new file mode 100644 index 0000000..b6dbc38 --- /dev/null +++ b/user/src/bin/ch5_spawn1.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{spawn, wait, waitpid}; + +/// 程序行为:先后产生 3 个有特定返回值的程序,检查 waitpid 能够获取正确返回值。 + +/// 理想输出: +/// new child i +/// Test wait OK! +/// Test waitpid OK! + +#[no_mangle] +pub fn main() -> i32 { + let cpid = spawn("ch5_exit0\0"); + assert!(cpid >= 0, "child pid invalid"); + println!("new child {}", cpid); + let mut exit_code: i32 = 0; + let exit_pid = wait(&mut exit_code); + assert_eq!(exit_pid, cpid, "error exit pid"); + assert_eq!(exit_code, 66778, "error exit code"); + println!("Test wait OK!"); + let (cpid0, cpid1) = (spawn("ch5_exit0\0"), spawn("ch5_exit1\0")); + let exit_pid = waitpid(cpid1 as usize, &mut exit_code); + assert_eq!(exit_pid, cpid1, "error exit pid"); + assert_eq!(exit_code, -233, "error exit code"); + let exit_pid = wait(&mut exit_code); + assert_eq!(exit_pid, cpid0, "error exit pid"); + assert_eq!(exit_code, 66778, "error exit code"); + println!("Test waitpid OK!"); + 0 +} diff --git a/user/src/bin/ch5_stride.rs b/user/src/bin/ch5_stride.rs new file mode 100644 index 0000000..ca44875 --- /dev/null +++ b/user/src/bin/ch5_stride.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +static TESTS: &[&str] = &[ + "ch5_stride0\0", + "ch5_stride1\0", + "ch5_stride2\0", + "ch5_stride3\0", + "ch5_stride4\0", + "ch5_stride5\0", +]; + + +use user_lib::{spawn, waitpid, set_priority}; + +#[no_mangle] +pub fn main() -> i32 { + let mut pid = [0; 6]; + let mut i = 0; + for test in TESTS { + pid[i] = spawn(*test); + i += 1; + } + set_priority(4); + for i in 0..6{ + let mut xstate: i32 = Default::default(); + let wait_pid = waitpid(pid[i] as usize, &mut xstate); + assert_eq!(pid[i], wait_pid); + } + 0 +} diff --git a/user/src/bin/ch5_stride0.rs b/user/src/bin/ch5_stride0.rs new file mode 100644 index 0000000..718e28a --- /dev/null +++ b/user/src/bin/ch5_stride0.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{get_time, set_priority}; + +/* +理想结果:6个进程退出时,输出 count 基本正比于 priority +*/ + +fn spin_delay() { + let mut j = true; + for _ in 0..10 { + j = !j; + } +} + +// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. +const MAX_TIME: isize = 4000; +pub fn count_during(prio: isize) -> isize { + let start_time = get_time(); + let mut acc = 0; + set_priority(prio); + loop { + spin_delay(); + acc += 1; + if acc % 400 == 0 { + let time = get_time() - start_time; + if time > MAX_TIME { + return acc; + } + } + } +} + +#[no_mangle] +pub fn main() -> usize { + let prio = 5; + let count = count_during(prio); + println!("priority = {}, exitcode = {}, ratio = {}", prio, count, count/prio); + 0 +} diff --git a/user/src/bin/ch5_stride1.rs b/user/src/bin/ch5_stride1.rs new file mode 100644 index 0000000..9c00caf --- /dev/null +++ b/user/src/bin/ch5_stride1.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{get_time, set_priority}; + +fn spin_delay() { + let mut j = true; + for _ in 0..10 { + j = !j; + } +} + +// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. +const MAX_TIME: isize = 4000; +fn count_during(prio: isize) -> isize { + let start_time = get_time(); + let mut acc = 0; + set_priority(prio); + loop { + spin_delay(); + acc += 1; + if acc % 400 == 0 { + let time = get_time() - start_time; + if time > MAX_TIME { + return acc; + } + } + } +} + +#[no_mangle] +pub fn main() -> usize { + let prio = 6; + let count = count_during(prio); + println!("priority = {}, exitcode = {}, ratio = {}", prio, count, count/prio); + 0 +} \ No newline at end of file diff --git a/user/src/bin/ch5_stride2.rs b/user/src/bin/ch5_stride2.rs new file mode 100644 index 0000000..ac35682 --- /dev/null +++ b/user/src/bin/ch5_stride2.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{get_time, set_priority}; + +fn spin_delay() { + let mut j = true; + for _ in 0..10 { + j = !j; + } +} + +// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. +const MAX_TIME: isize = 4000; +fn count_during(prio: isize) -> isize { + let start_time = get_time(); + let mut acc = 0; + set_priority(prio); + loop { + spin_delay(); + acc += 1; + if acc % 400 == 0 { + let time = get_time() - start_time; + if time > MAX_TIME { + return acc; + } + } + } +} + +#[no_mangle] +pub fn main() -> usize { + let prio = 7; + let count = count_during(prio); + println!("priority = {}, exitcode = {}, ratio = {}", prio, count, count/prio); + 0 +} \ No newline at end of file diff --git a/user/src/bin/ch5_stride3.rs b/user/src/bin/ch5_stride3.rs new file mode 100644 index 0000000..87b9851 --- /dev/null +++ b/user/src/bin/ch5_stride3.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{get_time, set_priority}; + +fn spin_delay() { + let mut j = true; + for _ in 0..10 { + j = !j; + } +} + +// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. +const MAX_TIME: isize = 4000; +fn count_during(prio: isize) -> isize { + let start_time = get_time(); + let mut acc = 0; + set_priority(prio); + loop { + spin_delay(); + acc += 1; + if acc % 400 == 0 { + let time = get_time() - start_time; + if time > MAX_TIME { + return acc; + } + } + } +} + +#[no_mangle] +pub fn main() -> usize { + let prio = 8; + let count = count_during(prio); + println!("priority = {}, exitcode = {}, ratio = {}", prio, count, count/prio); + 0 +} diff --git a/user/src/bin/ch5_stride4.rs b/user/src/bin/ch5_stride4.rs new file mode 100644 index 0000000..cf01c03 --- /dev/null +++ b/user/src/bin/ch5_stride4.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{get_time, set_priority}; + +fn spin_delay() { + let mut j = true; + for _ in 0..10 { + j = !j; + } +} + +// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. +const MAX_TIME: isize = 4000; +fn count_during(prio: isize) -> isize { + let start_time = get_time(); + let mut acc = 0; + set_priority(prio); + loop { + spin_delay(); + acc += 1; + if acc % 400 == 0 { + let time = get_time() - start_time; + if time > MAX_TIME { + return acc; + } + } + } +} + +#[no_mangle] +pub fn main() -> usize { + let prio = 9; + let count = count_during(prio); + println!("priority = {}, exitcode = {}, ratio = {}", prio, count, count/prio); + 0 +} \ No newline at end of file diff --git a/user/src/bin/ch5_stride5.rs b/user/src/bin/ch5_stride5.rs new file mode 100644 index 0000000..aba1363 --- /dev/null +++ b/user/src/bin/ch5_stride5.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{get_time, set_priority}; + +fn spin_delay() { + let mut j = true; + for _ in 0..10 { + j = !j; + } +} + +// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. +const MAX_TIME: isize = 4000; +fn count_during(prio: isize) -> isize { + let start_time = get_time(); + let mut acc = 0; + set_priority(prio); + loop { + spin_delay(); + acc += 1; + if acc % 400 == 0 { + let time = get_time() - start_time; + if time > MAX_TIME { + return acc; + } + } + } +} + +#[no_mangle] +pub fn main() -> usize { + let prio = 10; + let count = count_during(prio); + println!("priority = {}, exitcode = {}, ratio = {}", prio, count, count/prio); + 0 +} \ No newline at end of file diff --git a/user/src/bin/ch5_usertest.rs b/user/src/bin/ch5_usertest.rs new file mode 100644 index 0000000..1ee95be --- /dev/null +++ b/user/src/bin/ch5_usertest.rs @@ -0,0 +1,61 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +static TESTS: &[&str] = &[ + "ch2b_hello_world\0", + "ch2b_power_3\0", + "ch2b_power_5\0", + "ch2b_power_7\0", + "ch3b_yield0\0", + "ch3b_yield1\0", + "ch3b_yield2\0", + "ch3b_sleep\0", + "ch3b_sleep1\0", + "ch4_mmap0\0", + "ch4_mmap1\0", + "ch4_mmap2\0", + "ch4_mmap3\0", + "ch4_unmap\0", + "ch4_unmap2\0", + "ch5_spawn0\0", + "ch5_spawn1\0", + "ch5_setprio\0", + // "ch5_stride\0", +]; +static STEST: &str = "ch5_stride\0"; + +use user_lib::{spawn, waitpid}; + +/// 辅助测例,运行所有其他测例。 + +#[no_mangle] +pub fn main() -> i32 { + let mut pid = [0; 20]; + for (i, &test) in TESTS.iter().enumerate() { + println!("Usertests: Running {}", test); + pid[i] = spawn(test); + } + let mut xstate: i32 = Default::default(); + for (i, &test) in TESTS.iter().enumerate() { + let wait_pid = waitpid(pid[i] as usize, &mut xstate); + assert_eq!(pid[i], wait_pid); + println!( + "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", + test, pid[i], xstate + ); + } + println!("Usertests: Running {}", STEST); + let spid = spawn(STEST); + xstate = Default::default(); + let wait_pid = waitpid(spid as usize, &mut xstate); + assert_eq!(spid, wait_pid); + println!( + "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", + STEST, spid, xstate + ); + println!("ch5 Usertests passed!"); + 0 +} diff --git a/user/src/bin/ch5b_exit.rs b/user/src/bin/ch5b_exit.rs new file mode 100644 index 0000000..60510c9 --- /dev/null +++ b/user/src/bin/ch5b_exit.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{exit, fork, wait, waitpid, yield_}; + +const MAGIC: i32 = -0x10384; + +#[no_mangle] +pub fn main() -> i32 { + println!("I am the parent. Forking the child..."); + let pid = fork(); + if pid == 0 { + println!("I am the child."); + for _ in 0..7 { + yield_(); + } + exit(MAGIC); + } else { + println!("I am parent, fork a child pid {}", pid); + } + println!("I am the parent, waiting now.."); + let mut xstate: i32 = 0; + assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == MAGIC); + assert!(waitpid(pid as usize, &mut xstate) < 0 && wait(&mut xstate) <= 0); + println!("waitpid {} ok.", pid); + println!("exit pass."); + 0 +} diff --git a/user/src/bin/ch5b_forktest.rs b/user/src/bin/ch5b_forktest.rs new file mode 100644 index 0000000..22b53a9 --- /dev/null +++ b/user/src/bin/ch5b_forktest.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exit, fork, wait}; + +const MAX_CHILD: usize = 40; + +#[no_mangle] +pub fn main() -> i32 { + for i in 0..MAX_CHILD { + let pid = fork(); + if pid == 0 { + println!("I am child {}", i); + exit(0); + } else { + println!("forked child pid = {}", pid); + } + assert!(pid > 0); + } + let mut exit_code: i32 = 0; + for _ in 0..MAX_CHILD { + if wait(&mut exit_code) <= 0 { + panic!("wait stopped early"); + } + } + if wait(&mut exit_code) > 0 { + panic!("wait got too many"); + } + println!("forktest pass."); + 0 +} diff --git a/user/src/bin/ch5b_forktest2.rs b/user/src/bin/ch5b_forktest2.rs new file mode 100644 index 0000000..c91ce15 --- /dev/null +++ b/user/src/bin/ch5b_forktest2.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exit, fork, get_time, getpid, sleep, wait}; + +static NUM: usize = 30; + +#[no_mangle] +pub fn main() -> i32 { + for _ in 0..NUM { + let pid = fork(); + if pid == 0 { + let current_time = get_time(); + let sleep_length = + (current_time as i32 as isize) * (current_time as i32 as isize) % 1000 + 1000; + println!("pid {} sleep for {} ms", getpid(), sleep_length); + sleep(sleep_length as usize); + println!("pid {} OK!", getpid()); + exit(0); + } + } + + let mut exit_code: i32 = 0; + for _ in 0..NUM { + assert!(wait(&mut exit_code) > 0); + assert_eq!(exit_code, 0); + } + assert!(wait(&mut exit_code) < 0); + println!("forktest2 test passed!"); + 0 +} diff --git a/user/src/bin/ch5b_forktest_simple.rs b/user/src/bin/ch5b_forktest_simple.rs new file mode 100644 index 0000000..29a624b --- /dev/null +++ b/user/src/bin/ch5b_forktest_simple.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{fork, getpid, wait}; + +#[no_mangle] +pub fn main() -> i32 { + assert_eq!(wait(&mut 0i32), -1); + println!("sys_wait without child process test passed!"); + println!("parent start, pid = {}!", getpid()); + let pid = fork(); + if pid == 0 { + // child process + println!("hello child process!"); + 100 + } else { + // parent process + let mut exit_code: i32 = 0; + println!("ready waiting on parent process!"); + assert_eq!(pid, wait(&mut exit_code)); + assert_eq!(exit_code, 100); + println!("child process pid = {}, exit code = {}", pid, exit_code); + 0 + } +} diff --git a/user/src/bin/ch5b_forktree.rs b/user/src/bin/ch5b_forktree.rs new file mode 100644 index 0000000..bfcfc4c --- /dev/null +++ b/user/src/bin/ch5b_forktree.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exit, fork, getpid, sleep, yield_}; + +const DEPTH: usize = 4; + +fn fork_child(cur: &str, branch: char) { + let mut next = [0u8; DEPTH + 1]; + let l = cur.len(); + if l >= DEPTH { + return; + } + next[..l].copy_from_slice(cur.as_bytes()); + next[l] = branch as u8; + if fork() == 0 { + fork_tree(core::str::from_utf8(&next[..l + 1]).unwrap()); + yield_(); + exit(0); + } +} + +fn fork_tree(cur: &str) { + println!("pid{}: {}", getpid(), cur); + fork_child(cur, '0'); + fork_child(cur, '1'); +} + +#[no_mangle] +pub fn main() -> i32 { + fork_tree(""); + sleep(3000); + 0 +} diff --git a/user/src/bin/ch5b_initproc.rs b/user/src/bin/ch5b_initproc.rs new file mode 100644 index 0000000..7fe93d8 --- /dev/null +++ b/user/src/bin/ch5b_initproc.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, wait, yield_}; + +#[no_mangle] +fn main() -> i32 { + if fork() == 0 { + exec("ch5b_user_shell\0", &[0 as *const u8]); + } else { + loop { + let mut exit_code: i32 = 0; + let pid = wait(&mut exit_code); + if pid == -1 { + yield_(); + continue; + } + println!( + "[initproc] Released a zombie process, pid={}, exit_code={}", + pid, exit_code, + ); + } + } + 0 +} diff --git a/user/src/bin/ch5b_user_shell.rs b/user/src/bin/ch5b_user_shell.rs new file mode 100644 index 0000000..ea7ae52 --- /dev/null +++ b/user/src/bin/ch5b_user_shell.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +const DL: u8 = 0x7fu8; +const BS: u8 = 0x08u8; + +use alloc::string::String; +use user_lib::console::getchar; +use user_lib::{exec, flush, fork, waitpid}; + +#[no_mangle] +pub fn main() -> i32 { + println!("Rust user shell"); + let mut line: String = String::new(); + print!(">> "); + flush(); + loop { + let c = getchar(); + match c { + LF | CR => { + print!("\n"); + if !line.is_empty() { + line.push('\0'); + let pid = fork(); + if pid == 0 { + // child process + if exec(line.as_str(), &[0 as *const u8]) == -1 { + println!("Error when executing!"); + return -4; + } + unreachable!(); + } else { + let mut exit_code: i32 = 0; + let exit_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, exit_pid); + println!("Shell: Process {} exited with code {}", pid, exit_code); + } + line.clear(); + } + print!(">> "); + flush(); + } + BS | DL => { + if !line.is_empty() { + print!("{}", BS as char); + print!(" "); + print!("{}", BS as char); + flush(); + line.pop(); + } + } + _ => { + print!("{}", c as char); + flush(); + line.push(c as char); + } + } + } +} diff --git a/user/src/bin/ch6_file0.rs b/user/src/bin/ch6_file0.rs new file mode 100644 index 0000000..0b61fc9 --- /dev/null +++ b/user/src/bin/ch6_file0.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{close, open, read, write, OpenFlags}; + +/// 测试文件基本读写,输出 Test file0 OK! 就算正确。 + +#[no_mangle] +pub fn main() -> i32 { + let test_str = "Hello, world!"; + let fname = "fname\0"; + let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY); + assert!(fd > 0); + let fd = fd as usize; + write(fd, test_str.as_bytes()); + close(fd); + + let fd = open(fname, OpenFlags::RDONLY); + assert!(fd > 0); + let fd = fd as usize; + let mut buffer = [0u8; 100]; + let read_len = read(fd, &mut buffer) as usize; + close(fd); + + assert_eq!(test_str, core::str::from_utf8(&buffer[..read_len]).unwrap(),); + println!("Test file0 OK!"); + 0 +} diff --git a/user/src/bin/ch6_file1.rs b/user/src/bin/ch6_file1.rs new file mode 100644 index 0000000..6565121 --- /dev/null +++ b/user/src/bin/ch6_file1.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{close, fstat, open, OpenFlags, Stat, StatMode}; + +/// 测试 fstat,输出 Test fstat OK! 就算正确。 + +#[no_mangle] +pub fn main() -> i32 { + let fname = "fname1\0"; + let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY); + assert!(fd > 0); + let fd = fd as usize; + let stat: Stat = Stat::new(); + let ret = fstat(fd, &stat); + assert_eq!(ret, 0); + assert_eq!(stat.mode, StatMode::FILE); + assert_eq!(stat.nlink, 1); + close(fd); + // unlink(fname); + // It's recommended to rebuild the disk image. This program will not clean the file "fname1". + println!("Test fstat OK!"); + 0 +} diff --git a/user/src/bin/ch6_file2.rs b/user/src/bin/ch6_file2.rs new file mode 100644 index 0000000..c646314 --- /dev/null +++ b/user/src/bin/ch6_file2.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{close, fstat, link, open, read, unlink, write, OpenFlags, Stat}; + +/// 测试 link/unlink,输出 Test link OK! 就算正确。 + +#[no_mangle] +pub fn main() -> i32 { + let test_str = "Hello, world!"; + let fname = "fname2\0"; + let (lname0, lname1, lname2) = ("linkname0\0", "linkname1\0", "linkname2\0"); + let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY) as usize; + link(fname, lname0); + let stat = Stat::new(); + fstat(fd, &stat); + assert_eq!(stat.nlink, 2); + link(fname, lname1); + link(fname, lname2); + fstat(fd, &stat); + assert_eq!(stat.nlink, 4); + write(fd, test_str.as_bytes()); + close(fd); + + unlink(fname); + let fd = open(lname0, OpenFlags::RDONLY) as usize; + let stat2 = Stat::new(); + let mut buf = [0u8; 100]; + let read_len = read(fd, &mut buf) as usize; + assert_eq!(test_str, core::str::from_utf8(&buf[..read_len]).unwrap(),); + fstat(fd, &stat2); + assert_eq!(stat2.dev, stat.dev); + assert_eq!(stat2.ino, stat.ino); + assert_eq!(stat2.nlink, 3); + unlink(lname1); + unlink(lname2); + fstat(fd, &stat2); + assert_eq!(stat2.nlink, 1); + close(fd); + unlink(lname0); + // It's Ok if you don't delete the inode and data blocks. + println!("Test link OK!"); + 0 +} diff --git a/user/src/bin/ch6_file3.rs b/user/src/bin/ch6_file3.rs new file mode 100644 index 0000000..2c21a4a --- /dev/null +++ b/user/src/bin/ch6_file3.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{close, open, unlink, write, OpenFlags}; + +/// 测试大量 open/unlink,输出 Test mass open/unlink OK! 就算正确。 + +#[no_mangle] +pub fn main() -> i32 { + let test_str = "some random long long long long long long long long string".repeat(50); + let fname = "fname3\0"; + for i in 0..10 { + let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY); + if fd == -1 { + panic!("failed to crate file"); + } + let fd = fd as usize; + for _ in 0..50 { + write(fd, test_str.as_bytes()); + } + close(fd); + assert_eq!(unlink(fname), 0); + let fd = open(fname, OpenFlags::RDONLY); + assert!(fd < 0); + println!("test iteration {}", i) + } + println!("Test mass open/unlink OK!"); + 0 +} diff --git a/user/src/bin/ch6_usertest.rs b/user/src/bin/ch6_usertest.rs new file mode 100644 index 0000000..e9bcb51 --- /dev/null +++ b/user/src/bin/ch6_usertest.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +static TESTS: &[&str] = &[ + "ch2b_hello_world\0", + "ch2b_power_3\0", + "ch2b_power_5\0", + "ch2b_power_7\0", + "ch3b_yield0\0", + "ch3b_yield1\0", + "ch3b_yield2\0", + "ch3b_sleep\0", + "ch3b_sleep1\0", + "ch4_mmap0\0", + "ch4_mmap1\0", + "ch4_mmap2\0", + "ch4_mmap3\0", + "ch4_unmap\0", + "ch4_unmap2\0", + "ch5b_forktest2\0", + "ch5_spawn0\0", + "ch5_spawn1\0", + "ch6_file0\0", + "ch6_file1\0", + "ch6_file2\0", + "ch6_file3\0", +]; + +use user_lib::{spawn, waitpid}; + +/// 辅助测例,运行所有其他测例。 + +#[no_mangle] +pub fn main() -> i32 { + for test in TESTS { + println!("Usertests: Running {}", test); + let pid = spawn(*test); + let mut xstate: i32 = Default::default(); + let wait_pid = waitpid(pid as usize, &mut xstate); + assert_eq!(pid, wait_pid); + println!( + "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", + test, pid, xstate + ); + } + println!("ch6 Usertests passed!"); + 0 +} diff --git a/user/src/bin/ch6b_cat.rs b/user/src/bin/ch6b_cat.rs new file mode 100644 index 0000000..f9b43e2 --- /dev/null +++ b/user/src/bin/ch6b_cat.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{ + open, + OpenFlags, + close, + read, +}; +use alloc::string::String; + +#[no_mangle] +pub fn main() -> i32 { + let fd = open("filea\0", OpenFlags::RDONLY); + if fd == -1 { + panic!("Error occured when opening file"); + } + let fd = fd as usize; + let mut buf = [0u8; 16]; + let mut s = String::new(); + loop { + let size = read(fd, &mut buf) as usize; + if size == 0 { break; } + s.push_str(core::str::from_utf8(&buf[..size]).unwrap()); + } + println!("{}", s); + close(fd); + 0 +} diff --git a/user/src/bin/ch6b_filetest_simple.rs b/user/src/bin/ch6b_filetest_simple.rs new file mode 100644 index 0000000..60fda6a --- /dev/null +++ b/user/src/bin/ch6b_filetest_simple.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{ + open, + close, + read, + write, + OpenFlags, +}; + +#[no_mangle] +pub fn main() -> i32 { + let test_str = "Hello, world!"; + let filea = "filea\0"; + let fd = open(filea, OpenFlags::CREATE | OpenFlags::WRONLY); + assert!(fd > 0); + let fd = fd as usize; + write(fd, test_str.as_bytes()); + close(fd); + + let fd = open(filea, OpenFlags::RDONLY); + assert!(fd > 0); + let fd = fd as usize; + let mut buffer = [0u8; 100]; + let read_len = read(fd, &mut buffer) as usize; + close(fd); + + assert_eq!( + test_str, + core::str::from_utf8(&buffer[..read_len]).unwrap(), + ); + println!("file_test passed!"); + 0 +} \ No newline at end of file diff --git a/user/src/bin/ch6b_initproc.rs b/user/src/bin/ch6b_initproc.rs new file mode 100644 index 0000000..008cd79 --- /dev/null +++ b/user/src/bin/ch6b_initproc.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, wait, yield_}; + +#[no_mangle] +fn main() -> i32 { + if fork() == 0 { + exec("ch6b_user_shell\0", &[0 as *const u8]); + } else { + loop { + let mut exit_code: i32 = 0; + let pid = wait(&mut exit_code); + if pid == -1 { + yield_(); + continue; + } + println!( + "[initproc] Released a zombie process, pid={}, exit_code={}", + pid, exit_code, + ); + } + } + 0 +} diff --git a/user/src/bin/ch6b_user_shell.rs b/user/src/bin/ch6b_user_shell.rs new file mode 100644 index 0000000..ea7ae52 --- /dev/null +++ b/user/src/bin/ch6b_user_shell.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +const DL: u8 = 0x7fu8; +const BS: u8 = 0x08u8; + +use alloc::string::String; +use user_lib::console::getchar; +use user_lib::{exec, flush, fork, waitpid}; + +#[no_mangle] +pub fn main() -> i32 { + println!("Rust user shell"); + let mut line: String = String::new(); + print!(">> "); + flush(); + loop { + let c = getchar(); + match c { + LF | CR => { + print!("\n"); + if !line.is_empty() { + line.push('\0'); + let pid = fork(); + if pid == 0 { + // child process + if exec(line.as_str(), &[0 as *const u8]) == -1 { + println!("Error when executing!"); + return -4; + } + unreachable!(); + } else { + let mut exit_code: i32 = 0; + let exit_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, exit_pid); + println!("Shell: Process {} exited with code {}", pid, exit_code); + } + line.clear(); + } + print!(">> "); + flush(); + } + BS | DL => { + if !line.is_empty() { + print!("{}", BS as char); + print!(" "); + print!("{}", BS as char); + flush(); + line.pop(); + } + } + _ => { + print!("{}", c as char); + flush(); + line.push(c as char); + } + } + } +} diff --git a/user/src/bin/ch7_usertest.rs b/user/src/bin/ch7_usertest.rs new file mode 100644 index 0000000..dae1b98 --- /dev/null +++ b/user/src/bin/ch7_usertest.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +static TESTS: &[&str] = &[ +]; + +use user_lib::{spawn, waitpid}; + +/// 辅助测例,运行所有其他测例。 + +#[no_mangle] +pub fn main() -> i32 { + for test in TESTS { + println!("Usertests: Running {}", test); + let pid = spawn(*test); + let mut xstate: i32 = Default::default(); + let wait_pid = waitpid(pid as usize, &mut xstate); + assert_eq!(pid, wait_pid); + println!( + "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", + test, pid, xstate + ); + } + println!("ch7 Usertests passed!"); + 0 +} diff --git a/user/src/bin/ch7b_cat.rs b/user/src/bin/ch7b_cat.rs new file mode 100644 index 0000000..84fb8a6 --- /dev/null +++ b/user/src/bin/ch7b_cat.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{ + open, + OpenFlags, + close, + read, +}; +use alloc::string::String; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + assert!(argc == 2); + let fd = open(argv[1], OpenFlags::RDONLY); + if fd == -1 { + panic!("Error occured when opening file"); + } + let fd = fd as usize; + let mut buf = [0u8; 16]; + let mut s = String::new(); + loop { + let size = read(fd, &mut buf) as usize; + if size == 0 { break; } + s.push_str(core::str::from_utf8(&buf[..size]).unwrap()); + } + println!("{}", s); + close(fd); + 0 +} diff --git a/user/src/bin/ch7b_initproc.rs b/user/src/bin/ch7b_initproc.rs new file mode 100644 index 0000000..f9a887d --- /dev/null +++ b/user/src/bin/ch7b_initproc.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, wait, yield_}; + +#[no_mangle] +fn main() -> i32 { + if fork() == 0 { + exec("ch7b_user_shell\0", &[0 as *const u8]); + } else { + loop { + let mut exit_code: i32 = 0; + let pid = wait(&mut exit_code); + if pid == -1 { + yield_(); + continue; + } + println!( + "[initproc] Released a zombie process, pid={}, exit_code={}", + pid, exit_code, + ); + } + } + 0 +} diff --git a/user/src/bin/ch7b_pipe_large_test.rs b/user/src/bin/ch7b_pipe_large_test.rs new file mode 100644 index 0000000..eeac104 --- /dev/null +++ b/user/src/bin/ch7b_pipe_large_test.rs @@ -0,0 +1,71 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::format; +use user_lib::{close, fork, get_time, pipe, read, wait, write}; + +const LENGTH: usize = 3000; +#[no_mangle] +pub fn main() -> i32 { + // create pipes + // parent write to child + let mut down_pipe_fd = [0usize; 2]; + // child write to parent + let mut up_pipe_fd = [0usize; 2]; + pipe(&mut down_pipe_fd); + pipe(&mut up_pipe_fd); + let mut random_str = [0u8; LENGTH]; + if fork() == 0 { + // close write end of down pipe + close(down_pipe_fd[1]); + // close read end of up pipe + close(up_pipe_fd[0]); + assert_eq!(read(down_pipe_fd[0], &mut random_str) as usize, LENGTH); + close(down_pipe_fd[0]); + let sum: usize = random_str.iter().map(|v| *v as usize).sum::(); + println!("sum = {}(child)", sum); + let sum_str = format!("{}", sum); + write(up_pipe_fd[1], sum_str.as_bytes()); + close(up_pipe_fd[1]); + println!("Child process exited!"); + 0 + } else { + // close read end of down pipe + close(down_pipe_fd[0]); + // close write end of up pipe + close(up_pipe_fd[1]); + // generate a long random string + for ch in random_str.iter_mut().take(LENGTH) { + *ch = get_time() as u8; + } + // send it + assert_eq!( + write(down_pipe_fd[1], &random_str) as usize, + random_str.len() + ); + // close write end of down pipe + close(down_pipe_fd[1]); + // calculate sum(parent) + let sum: usize = random_str.iter().map(|v| *v as usize).sum::(); + println!("sum = {}(parent)", sum); + // recv sum(child) + let mut child_result = [0u8; 32]; + let result_len = read(up_pipe_fd[0], &mut child_result) as usize; + close(up_pipe_fd[0]); + // check + assert_eq!( + sum, + str::parse::(core::str::from_utf8(&child_result[..result_len]).unwrap()) + .unwrap() + ); + let mut _unused: i32 = 0; + wait(&mut _unused); + println!("pipe_large_test passed!"); + 0 + } +} diff --git a/user/src/bin/ch7b_pipetest.rs b/user/src/bin/ch7b_pipetest.rs new file mode 100644 index 0000000..5436c63 --- /dev/null +++ b/user/src/bin/ch7b_pipetest.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{close, fork, pipe, read, wait, write}; + +static STR: &str = "Hello, world!"; + +#[no_mangle] +pub fn main() -> i32 { + // create pipe + let mut pipe_fd = [0usize; 2]; + pipe(&mut pipe_fd); + // read end + assert_eq!(pipe_fd[0], 3); + // write end + assert_eq!(pipe_fd[1], 4); + if fork() == 0 { + // child process, read from parent + // close write_end + close(pipe_fd[1]); + let mut buffer = [0u8; 32]; + let len_read = read(pipe_fd[0], &mut buffer) as usize; + // close read_end + close(pipe_fd[0]); + assert_eq!(core::str::from_utf8(&buffer[..len_read]).unwrap(), STR); + println!("Read OK, child process exited!"); + 0 + } else { + // parent process, write to child + // close read end + close(pipe_fd[0]); + assert_eq!(write(pipe_fd[1], STR.as_bytes()), STR.len() as isize); + // close write end + close(pipe_fd[1]); + let mut child_exit_code: i32 = 0; + wait(&mut child_exit_code); + assert_eq!(child_exit_code, 0); + println!("pipetest passed!"); + 0 + } +} diff --git a/user/src/bin/ch7b_run_pipe_test.rs b/user/src/bin/ch7b_run_pipe_test.rs new file mode 100644 index 0000000..08dfbc1 --- /dev/null +++ b/user/src/bin/ch7b_run_pipe_test.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, wait}; + +#[no_mangle] +pub fn main() -> i32 { + for i in 0..1000 { + if fork() == 0 { + exec("ch7b_pipe_large_test\0", &[0 as *const u8]); + } else { + let mut _unused: i32 = 0; + wait(&mut _unused); + println!("Iter {} OK.", i); + } + } + 0 +} diff --git a/user/src/bin/ch7b_user_shell.rs b/user/src/bin/ch7b_user_shell.rs new file mode 100644 index 0000000..23dbb1e --- /dev/null +++ b/user/src/bin/ch7b_user_shell.rs @@ -0,0 +1,130 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +const DL: u8 = 0x7fu8; +const BS: u8 = 0x08u8; + +use alloc::string::String; +use alloc::vec::Vec; +use user_lib::console::getchar; +use user_lib::{close, dup, exec, flush, fork, open, waitpid, OpenFlags}; + +#[no_mangle] +pub fn main() -> i32 { + println!("Rust user shell"); + let mut line: String = String::new(); + print!(">> "); + flush(); + loop { + let c = getchar(); + match c { + LF | CR => { + println!(""); + if !line.is_empty() { + let args: Vec<_> = line.as_str().split(' ').collect(); + let mut args_copy: Vec = args + .iter() + .map(|&arg| { + let mut string = String::new(); + string.push_str(arg); + string + }) + .collect(); + + args_copy.iter_mut().for_each(|string| { + string.push('\0'); + }); + + // redirect input + let mut input = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == "<\0") + { + input = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + // redirect output + let mut output = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == ">\0") + { + output = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + let mut args_addr: Vec<*const u8> = + args_copy.iter().map(|arg| arg.as_ptr()).collect(); + args_addr.push(0 as *const u8); + let pid = fork(); + if pid == 0 { + // input redirection + if !input.is_empty() { + let input_fd = open(input.as_str(), OpenFlags::RDONLY); + if input_fd == -1 { + println!("Error when opening file {}", input); + return -4; + } + let input_fd = input_fd as usize; + close(0); + assert_eq!(dup(input_fd), 0); + close(input_fd); + } + // output redirection + if !output.is_empty() { + let output_fd = + open(output.as_str(), OpenFlags::CREATE | OpenFlags::WRONLY); + if output_fd == -1 { + println!("Error when opening file {}", output); + return -4; + } + let output_fd = output_fd as usize; + close(1); + assert_eq!(dup(output_fd), 1); + close(output_fd); + } + // child process + if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 { + println!("Error when executing!"); + return -4; + } + unreachable!(); + } else { + let mut exit_code: i32 = 0; + let exit_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, exit_pid); + println!("Shell: Process {} exited with code {}", pid, exit_code); + } + line.clear(); + } + print!(">> "); + flush(); + } + BS | DL => { + if !line.is_empty() { + print!("{}", BS as char); + print!(" "); + print!("{}", BS as char); + flush(); + line.pop(); + } + } + _ => { + print!("{}", c as char); + flush(); + line.push(c as char); + } + } + } +} diff --git a/user/src/bin/ch7b_yield.rs b/user/src/bin/ch7b_yield.rs new file mode 100644 index 0000000..55032e4 --- /dev/null +++ b/user/src/bin/ch7b_yield.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{getpid, yield_}; + +#[no_mangle] +pub fn main() -> i32 { + println!("Hello, I am process {}.", getpid()); + for i in 0..5 { + yield_(); + println!("Back in process {}, iteration {}.", getpid(), i); + } + println!("yield pass."); + 0 +} \ No newline at end of file diff --git a/user/src/bin/ch8_deadlock_mutex1.rs b/user/src/bin/ch8_deadlock_mutex1.rs new file mode 100644 index 0000000..15a0f2d --- /dev/null +++ b/user/src/bin/ch8_deadlock_mutex1.rs @@ -0,0 +1,22 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{enable_deadlock_detect, mutex_blocking_create, mutex_lock, mutex_unlock}; + +// 理想结果:检测到死锁 + +#[no_mangle] +pub fn main() -> i32 { + enable_deadlock_detect(true); + let mid = mutex_blocking_create() as usize; + assert_eq!(mutex_lock(mid), 0); + assert_eq!(mutex_lock(mid), -0xdead); + mutex_unlock(mid); + println!("deadlock test mutex 1 OK!"); + 0 +} diff --git a/user/src/bin/ch8_deadlock_sem1.rs b/user/src/bin/ch8_deadlock_sem1.rs new file mode 100644 index 0000000..21b1f95 --- /dev/null +++ b/user/src/bin/ch8_deadlock_sem1.rs @@ -0,0 +1,102 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{ + enable_deadlock_detect, exit, semaphore_create, semaphore_down, semaphore_up, sleep, +}; +use user_lib::{gettid, thread_create, waittid}; + +// sem 0: used to sync child thread with main +// sem 1-3: representing some kind of resource + +// 理想结果:检测到死锁,至少有一个子线程返回值不为 0 + +const SEM_BARRIER: usize = 0; +const THREAD_N: usize = 3; +const RES_TYPE: usize = 3; +const RES_NUM: [usize; RES_TYPE] = [1, 2, 1]; +const REQUEST: [Option; THREAD_N] = [Some(1), Some(3), Some(2)]; + +fn try_sem_down(sem_id: usize) { + if semaphore_down(sem_id) == -0xdead { + sem_dealloc(gettid() as usize); + println!("Deadlock detected. Test 08_sem1 failed!"); + exit(-1); + } +} + +fn sem_alloc(tid: usize) { + match tid { + 1 => assert_eq!(semaphore_down(2), 0), + 2 => { + assert_eq!(semaphore_down(1), 0); + assert_eq!(semaphore_down(2), 0); + } + 3 => assert_eq!(semaphore_down(3), 0), + _ => exit(1), + } + semaphore_down(SEM_BARRIER); +} + +fn sem_dealloc(tid: usize) { + semaphore_up(SEM_BARRIER); + match tid { + 1 => semaphore_up(2), + 2 => { + semaphore_up(1); + semaphore_up(2); + } + 3 => semaphore_up(3), + _ => exit(1), + } +} + +fn deadlock_test() { + let tid = gettid() as usize; + println!("thread {} running", tid); + sem_alloc(tid); + if let Some(sem_id) = REQUEST[tid - 1] { + try_sem_down(sem_id); + semaphore_up(sem_id); + } + sem_dealloc(tid); + println!("thread {} exited", tid); + exit(0); +} + +#[no_mangle] +pub fn main() -> i32 { + enable_deadlock_detect(true); + assert_eq!(semaphore_create(THREAD_N) as usize, SEM_BARRIER); + for _ in 0..THREAD_N { + semaphore_down(SEM_BARRIER); + } + + for n in RES_NUM { + semaphore_create(n); + } + let mut tids = [0; THREAD_N]; + + for i in 0..THREAD_N { + tids[i] = thread_create(deadlock_test as usize, 0) as usize; + } + + sleep(500); + for _ in 0..THREAD_N { + semaphore_up(SEM_BARRIER); + } + let mut failed = 0; + for tid in tids { + if waittid(tid) != 0 { + failed += 1; + } + } + assert!(failed > 0); + println!("deadlock test semaphore 1 OK!"); + 0 +} diff --git a/user/src/bin/ch8_deadlock_sem2.rs b/user/src/bin/ch8_deadlock_sem2.rs new file mode 100644 index 0000000..ff653ea --- /dev/null +++ b/user/src/bin/ch8_deadlock_sem2.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{ + enable_deadlock_detect, exit, semaphore_create, semaphore_down, semaphore_up, sleep, +}; +use user_lib::{gettid, thread_create, waittid}; + +// sem 0: used to sync child thread with main +// sem 1-3: representing some kind of resource + +// 理想结果:未检测到死锁,子线程返回值均为 0 + +const THREAD_N: usize = 4; +const RES_TYPE: usize = 2; +const RES_NUM: [usize; RES_TYPE] = [2, 2]; +const ALLOC: [usize; THREAD_N] = [2, 1, 1, 2]; +const REQUEST: [Option; THREAD_N] = [Some(1), None, Some(2), None]; + +fn try_sem_down(sem_id: usize) { + if semaphore_down(sem_id) == -0xdead { + semaphore_up(ALLOC[(gettid() - 1) as usize]); + exit(-1); + } +} + +fn deadlock_test() { + let id = (gettid() - 1) as usize; + assert_eq!(semaphore_down(ALLOC[id]), 0); + semaphore_down(0); + if let Some(sem_id) = REQUEST[id] { + try_sem_down(sem_id); + semaphore_up(sem_id); + } + semaphore_up(ALLOC[id]); + exit(0); +} + +#[no_mangle] +pub fn main() -> i32 { + enable_deadlock_detect(true); + semaphore_create(THREAD_N); + for _ in 0..THREAD_N { + semaphore_down(0); + } + + for n in RES_NUM { + semaphore_create(n); + } + let mut tids = [0; THREAD_N]; + + for i in 0..THREAD_N { + tids[i] = thread_create(deadlock_test as usize, 0) as usize; + } + + sleep(1000); + for _ in 0..THREAD_N { + semaphore_up(0); + } + + let mut failed = 0; + for tid in tids { + if waittid(tid) != 0 { + failed += 1; + } + } + + assert_eq!(failed, 0); + println!("deadlock test semaphore 2 OK!"); + 0 +} diff --git a/user/src/bin/ch8_usertest.rs b/user/src/bin/ch8_usertest.rs new file mode 100644 index 0000000..79ec4af --- /dev/null +++ b/user/src/bin/ch8_usertest.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +const TESTS: &[&str] = &[ + "ch2b_hello_world\0", + "ch2b_power_3\0", + "ch2b_power_5\0", + "ch2b_power_7\0", + "ch3b_sleep\0", + "ch3b_sleep1\0", + "ch3b_yield0\0", + "ch3b_yield1\0", + "ch3b_yield2\0", + "ch5b_forktest2\0", + "ch6b_filetest_simple\0", + "ch7b_pipetest\0", + "ch8_deadlock_mutex1\0", + "ch8_deadlock_sem1\0", + "ch8_deadlock_sem2\0", + "ch8b_mpsc_sem\0", + "ch8b_phil_din_mutex\0", + "ch8b_race_adder_mutex_spin\0", + "ch8b_sync_sem\0", + "ch8b_test_condvar\0", + "ch8b_threads\0", + "ch8b_threads_arg\0", +]; + +const TEST_NUM: usize = TESTS.len(); + +use user_lib::{exec, fork, waitpid}; + +#[no_mangle] +pub fn main() -> i32 { + let mut pids = [0; TEST_NUM]; + for (i, &test) in TESTS.iter().enumerate() { + println!("Usertests: Running {}", test); + let pid = fork(); + if pid == 0 { + exec(&*test, &[core::ptr::null::()]); + panic!("unreachable!"); + } else { + pids[i] = pid; + } + } + let mut xstate: i32 = Default::default(); + for (i, &test) in TESTS.iter().enumerate() { + let wait_pid = waitpid(pids[i] as usize, &mut xstate); + assert_eq!(pids[i], wait_pid); + println!( + "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", + test, pids[i], xstate + ); + } + println!("ch8 Usertests passed!"); + 0 +} diff --git a/user/src/bin/ch8b_initproc.rs b/user/src/bin/ch8b_initproc.rs new file mode 100644 index 0000000..1e4011d --- /dev/null +++ b/user/src/bin/ch8b_initproc.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, wait, yield_}; + +#[no_mangle] +fn main() -> i32 { + if fork() == 0 { + exec("ch7b_user_shell\0", &[core::ptr::null::()]); + } else { + loop { + let mut exit_code: i32 = 0; + let pid = wait(&mut exit_code); + if pid == -1 { + yield_(); + continue; + } + println!( + "[initproc] Released a zombie process, pid={}, exit_code={}", + pid, exit_code, + ); + } + } + 0 +} diff --git a/user/src/bin/ch8b_mpsc_sem.rs b/user/src/bin/ch8b_mpsc_sem.rs new file mode 100644 index 0000000..7b92b9b --- /dev/null +++ b/user/src/bin/ch8b_mpsc_sem.rs @@ -0,0 +1,73 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::exit; +use user_lib::{semaphore_create, semaphore_down, semaphore_up}; +use user_lib::{thread_create, waittid}; + +const SEM_MUTEX: usize = 0; +const SEM_EMPTY: usize = 1; +const SEM_EXISTED: usize = 2; +const BUFFER_SIZE: usize = 8; +static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE]; +static mut FRONT: usize = 0; +static mut TAIL: usize = 0; +const PRODUCER_COUNT: usize = 4; +const NUMBER_PER_PRODUCER: usize = 100; + +unsafe fn producer(id: *const usize) -> ! { + let id = *id; + for _ in 0..NUMBER_PER_PRODUCER { + semaphore_down(SEM_EMPTY); + semaphore_down(SEM_MUTEX); + BUFFER[FRONT] = id; + FRONT = (FRONT + 1) % BUFFER_SIZE; + semaphore_up(SEM_MUTEX); + semaphore_up(SEM_EXISTED); + } + exit(0) +} + +unsafe fn consumer() -> ! { + for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER { + semaphore_down(SEM_EXISTED); + semaphore_down(SEM_MUTEX); + print!("{} ", BUFFER[TAIL]); + TAIL = (TAIL + 1) % BUFFER_SIZE; + semaphore_up(SEM_MUTEX); + semaphore_up(SEM_EMPTY); + } + println!(""); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphores + assert_eq!(semaphore_create(1) as usize, SEM_MUTEX); + assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY); + assert_eq!(semaphore_create(0) as usize, SEM_EXISTED); + // create threads + let ids: Vec<_> = (0..PRODUCER_COUNT).collect(); + let mut threads = Vec::new(); + for i in 0..PRODUCER_COUNT { + threads.push(thread_create( + producer as usize, + &ids.as_slice()[i] as *const _ as usize, + )); + } + threads.push(thread_create(consumer as usize, 0)); + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("mpsc_sem passed!"); + 0 +} diff --git a/user/src/bin/ch8b_phil_din_mutex.rs b/user/src/bin/ch8b_phil_din_mutex.rs new file mode 100644 index 0000000..7c0765b --- /dev/null +++ b/user/src/bin/ch8b_phil_din_mutex.rs @@ -0,0 +1,109 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, sleep_blocking}; +use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; +use user_lib::{thread_create, waittid}; + +const N: usize = 5; +const ROUND: usize = 4; +// A round: think -> wait for forks -> eat +const GRAPH_SCALE: usize = 10; + +fn get_time_u() -> usize { + get_time() as usize +} + +// Time unit: ms +const ARR: [[usize; ROUND * 2]; N] = [ + [70, 80, 100, 40, 50, 60, 20, 40], + [30, 60, 20, 70, 100, 10, 30, 60], + [50, 20, 90, 20, 40, 60, 120, 40], + [50, 100, 60, 50, 80, 60, 20, 90], + [60, 10, 60, 60, 20, 50, 60, 20], +]; +static mut THINK: [[usize; ROUND * 2]; N] = [[0; ROUND * 2]; N]; +static mut EAT: [[usize; ROUND * 2]; N] = [[0; ROUND * 2]; N]; + +fn philosopher_dining_problem(id: *const usize) { + let id = unsafe { *id }; + let left = id; + let right = if id == N - 1 { 0 } else { id + 1 }; + let min = if left < right { left } else { right }; + let max = left + right - min; + for round in 0..ROUND { + // thinking + unsafe { + THINK[id][2 * round] = get_time_u(); + } + sleep_blocking(ARR[id][2 * round]); + unsafe { + THINK[id][2 * round + 1] = get_time_u(); + } + // wait for forks + mutex_lock(min); + mutex_lock(max); + // eating + unsafe { + EAT[id][2 * round] = get_time_u(); + } + sleep_blocking(ARR[id][2 * round + 1]); + unsafe { + EAT[id][2 * round + 1] = get_time_u(); + } + mutex_unlock(max); + mutex_unlock(min); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + println!("Here comes {} philosophers!", N); + let mut v = Vec::new(); + let ids: Vec<_> = (0..N).collect(); + let start = get_time_u(); + for i in 0..N { + assert_eq!(mutex_blocking_create(), i as isize); + v.push(thread_create( + philosopher_dining_problem as usize, + &ids.as_slice()[i] as *const _ as usize, + )); + } + for tid in v.iter() { + waittid(*tid as usize); + } + let time_cost = get_time_u() - start; + println!("time cost = {}", time_cost); + println!("'-' -> THINKING; 'x' -> EATING; ' ' -> WAITING "); + for id in (0..N).into_iter().chain(0..=0) { + print!("#{}:", id); + for j in 0..time_cost / GRAPH_SCALE { + let current_time = j * GRAPH_SCALE + start; + if (0..ROUND).any(|round| unsafe { + let start_thinking = THINK[id][2 * round]; + let end_thinking = THINK[id][2 * round + 1]; + start_thinking <= current_time && current_time <= end_thinking + }) { + print!("-"); + } else if (0..ROUND).any(|round| unsafe { + let start_eating = EAT[id][2 * round]; + let end_eating = EAT[id][2 * round + 1]; + start_eating <= current_time && current_time <= end_eating + }) { + print!("x"); + } else { + print!(" "); + }; + } + println!(""); + } + println!("philosopher dining problem with mutex test passed!"); + 0 +} diff --git a/user/src/bin/ch8b_race_adder.rs b/user/src/bin/ch8b_race_adder.rs new file mode 100644 index 0000000..139e137 --- /dev/null +++ b/user/src/bin/ch8b_race_adder.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +const PER_THREAD: usize = 1000; +const THREAD_COUNT: usize = 16; + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + t = t * t % 10007; + } + a.write_volatile(cur + 1); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main() -> i32 { + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..THREAD_COUNT { + v.push(thread_create(f as usize, 0) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); + println!("race adder test passed!"); + 0 +} diff --git a/user/src/bin/ch8b_race_adder_atomic.rs b/user/src/bin/ch8b_race_adder_atomic.rs new file mode 100644 index 0000000..c00fe6a --- /dev/null +++ b/user/src/bin/ch8b_race_adder_atomic.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::sync::atomic::{AtomicBool, Ordering}; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static OCCUPIED: AtomicBool = AtomicBool::new(false); +const PER_THREAD: usize = 1000; +const THREAD_COUNT: usize = 16; + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + while OCCUPIED + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + { + yield_(); + } + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + t = t * t % 10007; + } + a.write_volatile(cur + 1); + OCCUPIED.store(false, Ordering::Relaxed); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main() -> i32 { + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..THREAD_COUNT { + v.push(thread_create(f as usize, 0) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); + println!("race adder using atomic test passed!"); + 0 +} diff --git a/user/src/bin/ch8b_race_adder_loop.rs b/user/src/bin/ch8b_race_adder_loop.rs new file mode 100644 index 0000000..11a0324 --- /dev/null +++ b/user/src/bin/ch8b_race_adder_loop.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static mut OCCUPIED: bool = false; +const PER_THREAD: usize = 1000; +const THREAD_COUNT: usize = 16; + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + while OCCUPIED { + yield_(); + } + OCCUPIED = true; + // enter critical section + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + t = t * t % 10007; + } + a.write_volatile(cur + 1); + // exit critical section + OCCUPIED = false; + } + + exit(t as i32) +} + +#[no_mangle] +pub fn main() -> i32 { + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..THREAD_COUNT { + v.push(thread_create(f as usize, 0) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); + println!("race adder using loop test passed!"); + 0 +} diff --git a/user/src/bin/ch8b_race_adder_mutex_spin.rs b/user/src/bin/ch8b_race_adder_mutex_spin.rs new file mode 100644 index 0000000..d2df338 --- /dev/null +++ b/user/src/bin/ch8b_race_adder_mutex_spin.rs @@ -0,0 +1,47 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid}; +use user_lib::{mutex_create, mutex_lock, mutex_unlock}; + +static mut A: usize = 0; +const PER_THREAD: usize = 1000; +const THREAD_COUNT: usize = 16; + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + mutex_lock(0); + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + t = t * t % 10007; + } + a.write_volatile(cur + 1); + mutex_unlock(0); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main() -> i32 { + let start = get_time(); + assert_eq!(mutex_create(), 0); + let mut v = Vec::new(); + for _ in 0..THREAD_COUNT { + v.push(thread_create(f as usize, 0) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); + println!("race adder using spin mutex test passed!"); + 0 +} diff --git a/user/src/bin/ch8b_stackful_coroutine.rs b/user/src/bin/ch8b_stackful_coroutine.rs new file mode 100644 index 0000000..2341547 --- /dev/null +++ b/user/src/bin/ch8b_stackful_coroutine.rs @@ -0,0 +1,346 @@ +// we porting below codes to Rcore Tutorial v3 +// https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/ +// https://github.com/cfsamson/example-greenthreads +#![no_std] +#![no_main] +#![feature(naked_functions)] + +extern crate alloc; +#[macro_use] +extern crate user_lib; + +use alloc::vec; +use alloc::vec::Vec; +use core::arch::asm; + +use user_lib::exit; + +// In our simple example we set most constraints here. +const DEFAULT_STACK_SIZE: usize = 4096; //128 got SEGFAULT, 256(1024, 4096) got right results. +const MAX_TASKS: usize = 5; +static mut RUNTIME: usize = 0; + +pub struct Runtime { + tasks: Vec, + current: usize, +} + +#[derive(PartialEq, Eq, Debug)] +enum State { + Available, + Running, + Ready, +} + +struct Task { + id: usize, + stack: Vec, + ctx: TaskContext, + state: State, +} + +#[derive(Debug, Default)] +#[repr(C)] // not strictly needed but Rust ABI is not guaranteed to be stable +pub struct TaskContext { + // 15 u64 + x1: u64, //ra: return addres + x2: u64, //sp + x8: u64, //s0,fp + x9: u64, //s1 + x18: u64, //x18-27: s2-11 + x19: u64, + x20: u64, + x21: u64, + x22: u64, + x23: u64, + x24: u64, + x25: u64, + x26: u64, + x27: u64, + nx1: u64, //new return addres +} + +impl Task { + fn new(id: usize) -> Self { + // We initialize each task here and allocate the stack. This is not neccesary, + // we can allocate memory for it later, but it keeps complexity down and lets us focus on more interesting parts + // to do it here. The important part is that once allocated it MUST NOT move in memory. + Task { + id, + stack: vec![0_u8; DEFAULT_STACK_SIZE], + ctx: TaskContext::default(), + state: State::Available, + } + } +} + +impl Runtime { + pub fn new() -> Self { + // This will be our base task, which will be initialized in the `running` state + let base_task = Task { + id: 0, + stack: vec![0_u8; DEFAULT_STACK_SIZE], + ctx: TaskContext::default(), + state: State::Running, + }; + + // We initialize the rest of our tasks. + let mut tasks = vec![base_task]; + let mut available_tasks: Vec = (1..MAX_TASKS).map(|i| Task::new(i)).collect(); + tasks.append(&mut available_tasks); + + Runtime { tasks, current: 0 } + } + + /// This is cheating a bit, but we need a pointer to our Runtime stored so we can call yield on it even if + /// we don't have a reference to it. + pub fn init(&self) { + unsafe { + let r_ptr: *const Runtime = self; + RUNTIME = r_ptr as usize; + } + } + + /// This is where we start running our runtime. If it is our base task, we call yield until + /// it returns false (which means that there are no tasks scheduled) and we are done. + pub fn run(&mut self) { + while self.t_yield() {} + println!("All tasks finished!"); + } + + /// This is our return function. The only place we use this is in our `guard` function. + /// If the current task is not our base task we set its state to Available. It means + /// we're finished with it. Then we yield which will schedule a new task to be run. + fn t_return(&mut self) { + if self.current != 0 { + self.tasks[self.current].state = State::Available; + self.t_yield(); + } + } + + /// This is the heart of our runtime. Here we go through all tasks and see if anyone is in the `Ready` state. + /// If no task is `Ready` we're all done. This is an extremely simple scheduler using only a round-robin algorithm. + /// + /// If we find a task that's ready to be run we change the state of the current task from `Running` to `Ready`. + /// Then we call switch which will save the current context (the old context) and load the new context + /// into the CPU which then resumes based on the context it was just passed. + /// + /// NOITCE: if we comment below `#[inline(never)]`, we can not get the corrent running result + #[inline(never)] + fn t_yield(&mut self) -> bool { + let mut pos = self.current; + while self.tasks[pos].state != State::Ready { + pos += 1; + if pos == self.tasks.len() { + pos = 0; + } + if pos == self.current { + return false; + } + } + + if self.tasks[self.current].state != State::Available { + self.tasks[self.current].state = State::Ready; + } + + 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); + } + + // NOTE: this might look strange and it is. Normally we would just mark this as `unreachable!()` but our compiler + // is too smart for it's own good so it optimized our code away on release builds. Curiously this happens on windows + // and not on linux. This is a common problem in tests so Rust has a `black_box` function in the `test` crate that + // will "pretend" to use a value we give it to prevent the compiler from eliminating code. I'll just do this instead, + // this code will never be run anyways and if it did it would always be `true`. + self.tasks.len() > 0 + } + + /// While `yield` is the logically interesting function I think this the technically most interesting. + /// + /// When we spawn a new task we first check if there are any available tasks (tasks in `Parked` state). + /// If we run out of tasks we panic in this scenario but there are several (better) ways to handle that. + /// We keep things simple for now. + /// + /// When we find an available task we get the stack length and a pointer to our u8 bytearray. + /// + /// The next part we have to use some unsafe functions. First we write an address to our `guard` function + /// that will be called if the function we provide returns. Then we set the address to the function we + /// pass inn. + /// + /// Third, we set the value of `sp` which is the stack pointer to the address of our provided function so we start + /// executing that first when we are scheuled to run. + /// + /// Lastly we set the state as `Ready` which means we have work to do and is ready to do it. + pub fn spawn(&mut self, f: fn()) { + 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); + + // make sure our stack itself is 8 byte aligned - it will always + // offset to a lower memory address. Since we know we're at the "high" + // memory address of our allocated space, we know that offsetting to + // a lower one will be a valid address (given that we actually allocated) + // enough space to actually get an aligned pointer in the first place). + let s_ptr = (s_ptr as usize & !7) as *mut u8; + + 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; + } +} + +/// This is our guard function that we place on top of the stack. All this function does is set the +/// state of our current task and then `yield` which will then schedule a new task to be run. +fn guard() { + unsafe { + let rt_ptr = RUNTIME as *mut Runtime; + (*rt_ptr).t_return(); + }; +} + +/// We know that Runtime is alive the length of the program and that we only access from one core +/// (so no datarace). We yield execution of the current task by dereferencing a pointer to our +/// Runtime and then calling `t_yield` +pub fn yield_task() { + unsafe { + let rt_ptr = RUNTIME as *mut Runtime; + (*rt_ptr).t_yield(); + }; +} + +/// So here is our inline Assembly. As you remember from our first example this is just a bit more elaborate where we first +/// read out the values of all the registers we need and then sets all the register values to the register values we +/// saved when we suspended exceution on the "new" task. +/// +/// This is essentially all we need to do to save and resume execution. +/// +/// Some details about inline assembly. +/// +/// The assembly commands in the string literal is called the assemblt template. It is preceeded by +/// zero or up to four segments indicated by ":": +/// +/// - First ":" we have our output parameters, this parameters that this function will return. +/// - Second ":" we have the input parameters which is our contexts. We only read from the "new" context +/// but we modify the "old" context saving our registers there (see volatile option below) +/// - Third ":" This our clobber list, this is information to the compiler that these registers can't be used freely +/// - Fourth ":" This is options we can pass inn, Rust has 3: "alignstack", "volatile" and "intel" +/// +/// For this to work on windows we need to use "alignstack" where the compiler adds the neccesary padding to +/// make sure our stack is aligned. Since we modify one of our inputs, our assembly has "side effects" +/// therefore we should use the `volatile` option. I **think** this is actually set for us by default +/// when there are no output parameters given (my own assumption after going through the source code) +/// for the `asm` macro, but we should make it explicit anyway. +/// +/// One last important part (it will not work without this) is the #[naked] attribute. Basically this lets us have full +/// control over the stack layout since normal functions has a prologue-and epilogue added by the +/// compiler that will cause trouble for us. We avoid this by marking the funtion as "Naked". +/// For this to work on `release` builds we also need to use the `#[inline(never)] attribute or else +/// the compiler decides to inline this function (curiously this currently only happens on Windows). +/// If the function is inlined we get a curious runtime error where it fails when switching back +/// to as saved context and in general our assembly will not work as expected. +/// +/// see: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md +/// see: https://doc.rust-lang.org/nightly/reference/inline-assembly.html +/// see: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html +#[naked] +#[no_mangle] +unsafe fn switch(old: *mut TaskContext, new: *const TaskContext) { + // a0: _old, a1: _new + asm!( + " + sd x1, 0x00(a0) + sd x2, 0x08(a0) + sd x8, 0x10(a0) + sd x9, 0x18(a0) + sd x18, 0x20(a0) + sd x19, 0x28(a0) + sd x20, 0x30(a0) + sd x21, 0x38(a0) + sd x22, 0x40(a0) + sd x23, 0x48(a0) + sd x24, 0x50(a0) + sd x25, 0x58(a0) + sd x26, 0x60(a0) + sd x27, 0x68(a0) + sd x1, 0x70(a0) + + ld x1, 0x00(a1) + ld x2, 0x08(a1) + ld x8, 0x10(a1) + ld x9, 0x18(a1) + ld x18, 0x20(a1) + ld x19, 0x28(a1) + ld x20, 0x30(a1) + ld x21, 0x38(a1) + ld x22, 0x40(a1) + ld x23, 0x48(a1) + ld x24, 0x50(a1) + ld x25, 0x58(a1) + ld x26, 0x60(a1) + ld x27, 0x68(a1) + ld t0, 0x70(a1) + + jr t0 + ", + options(noreturn) + ); +} + +#[no_mangle] +pub fn main() { + println!("stackful_coroutine begin..."); + println!("TASK 0(Runtime) STARTING"); + 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"); + }); + runtime.spawn(|| { + println!("TASK 2 STARTING"); + let id = 2; + for i in 0..8 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 2 FINISHED"); + }); + runtime.spawn(|| { + println!("TASK 3 STARTING"); + let id = 3; + for i in 0..12 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 3 FINISHED"); + }); + runtime.spawn(|| { + println!("TASK 4 STARTING"); + let id = 4; + for i in 0..16 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 4 FINISHED"); + }); + runtime.run(); + println!("stackful_coroutine PASSED"); + exit(0); +} diff --git a/user/src/bin/ch8b_stackless_coroutine.rs b/user/src/bin/ch8b_stackless_coroutine.rs new file mode 100644 index 0000000..43aeb2d --- /dev/null +++ b/user/src/bin/ch8b_stackless_coroutine.rs @@ -0,0 +1,129 @@ +// https://blog.aloni.org/posts/a-stack-less-rust-coroutine-100-loc/ +// https://github.com/chyyuu/example-coroutine-and-thread/tree/stackless-coroutine-x86 +#![no_std] +#![no_main] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::task::{RawWaker, RawWakerVTable, Waker}; + +extern crate alloc; +use alloc::collections::VecDeque; + +use alloc::boxed::Box; + +#[macro_use] +extern crate user_lib; + +enum State { + Halted, + Running, +} + +struct Task { + state: State, +} + +impl Task { + fn waiter<'a>(&'a mut self) -> Waiter<'a> { + Waiter { task: self } + } +} + +struct Waiter<'a> { + task: &'a mut Task, +} + +impl<'a> Future for Waiter<'a> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll { + match self.task.state { + State::Halted => { + self.task.state = State::Running; + Poll::Ready(()) + } + State::Running => { + self.task.state = State::Halted; + Poll::Pending + } + } + } +} + +struct Executor { + tasks: VecDeque>>>, +} + +impl Executor { + fn new() -> Self { + Executor { + tasks: VecDeque::new(), + } + } + + fn push(&mut self, closure: C) + where + F: Future + 'static, + C: FnOnce(Task) -> F, + { + let task = Task { + state: State::Running, + }; + self.tasks.push_back(Box::pin(closure(task))); + } + + fn run(&mut self) { + let waker = create_waker(); + let mut context = Context::from_waker(&waker); + + while let Some(mut task) = self.tasks.pop_front() { + match task.as_mut().poll(&mut context) { + Poll::Pending => { + self.tasks.push_back(task); + } + Poll::Ready(()) => {} + } + } + } +} + +pub fn create_waker() -> Waker { + // Safety: The waker points to a vtable with functions that do nothing. Doing + // nothing is memory-safe. + unsafe { Waker::from_raw(RAW_WAKER) } +} + +const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE); +const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); + +unsafe fn clone(_: *const ()) -> RawWaker { + RAW_WAKER +} +unsafe fn wake(_: *const ()) {} +unsafe fn wake_by_ref(_: *const ()) {} +unsafe fn drop(_: *const ()) {} + +#[no_mangle] +pub fn main() -> i32 { + println!("stackless coroutine Begin.."); + let mut exec = Executor::new(); + println!(" Create futures"); + for instance in 1..=3 { + exec.push(move |mut task| async move { + println!(" Task {}: begin state", instance); + task.waiter().await; + println!(" Task {}: next state", instance); + task.waiter().await; + println!(" Task {}: end state", instance); + }); + } + + println!(" Running"); + exec.run(); + println!(" Done"); + println!("stackless coroutine PASSED"); + + 0 +} diff --git a/user/src/bin/ch8b_sync_sem.rs b/user/src/bin/ch8b_sync_sem.rs new file mode 100644 index 0000000..3c6c575 --- /dev/null +++ b/user/src/bin/ch8b_sync_sem.rs @@ -0,0 +1,45 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{semaphore_create, semaphore_down, semaphore_up}; +use user_lib::{sleep_blocking, thread_create, waittid}; + +const SEM_SYNC: usize = 0; + +unsafe fn first() -> ! { + sleep_blocking(10); + println!("First work and wakeup Second"); + semaphore_up(SEM_SYNC); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait first"); + semaphore_down(SEM_SYNC); + println!("Second can work now"); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphores + assert_eq!(semaphore_create(0) as usize, SEM_SYNC); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("sync_sem passed!"); + 0 +} diff --git a/user/src/bin/ch8b_test_condvar.rs b/user/src/bin/ch8b_test_condvar.rs new file mode 100644 index 0000000..26029eb --- /dev/null +++ b/user/src/bin/ch8b_test_condvar.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{ + condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock, +}; +use user_lib::{sleep_blocking, thread_create, waittid}; + +static mut A: usize = 0; + +const CONDVAR_ID: usize = 0; +const MUTEX_ID: usize = 0; + +unsafe fn first() -> ! { + sleep_blocking(10); + println!("First work, Change A --> 1 and wakeup Second"); + mutex_lock(MUTEX_ID); + A = 1; + condvar_signal(CONDVAR_ID); + mutex_unlock(MUTEX_ID); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait A=1"); + mutex_lock(MUTEX_ID); + while A == 0 { + println!("Second: A is {}", A); + condvar_wait(CONDVAR_ID, MUTEX_ID); + } + mutex_unlock(MUTEX_ID); + println!("A is {}, Second can work now", A); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create condvar & mutex + assert_eq!(condvar_create() as usize, CONDVAR_ID); + assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("test_condvar passed!"); + 0 +} diff --git a/user/src/bin/ch8b_threads.rs b/user/src/bin/ch8b_threads.rs new file mode 100644 index 0000000..0db8292 --- /dev/null +++ b/user/src/bin/ch8b_threads.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec; +use user_lib::{exit, thread_create, waittid}; + +pub fn thread_a() -> ! { + let mut t = 2i32; + for _ in 0..1000 { + print!("a"); + for __ in 0..5000 { + t = t * t % 10007; + } + } + println!("{}", t); + exit(1) +} + +pub fn thread_b() -> ! { + let mut t = 2i32; + for _ in 0..1000 { + print!("b"); + for __ in 0..5000 { + t = t * t % 10007; + } + } + println!("{}", t); + exit(2) +} + +pub fn thread_c() -> ! { + let mut t = 2i32; + for _ in 0..1000 { + print!("c"); + for __ in 0..5000 { + t = t * t % 10007; + } + } + println!("{}", t); + exit(3) +} + +#[no_mangle] +pub fn main() -> i32 { + let v = vec![ + thread_create(thread_a as usize, 0), + thread_create(thread_b as usize, 0), + thread_create(thread_c as usize, 0), + ]; + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + println!("thread#{} exited with code {}", tid, exit_code); + assert_eq!(*tid, exit_code); + } + println!("main thread exited."); + println!("threads test passed!"); + 0 +} diff --git a/user/src/bin/ch8b_threads_arg.rs b/user/src/bin/ch8b_threads_arg.rs new file mode 100644 index 0000000..2c0de19 --- /dev/null +++ b/user/src/bin/ch8b_threads_arg.rs @@ -0,0 +1,45 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, thread_create, waittid}; + +struct Argument { + pub ch: char, + pub rc: i32, +} + +fn thread_print(arg: *const Argument) -> ! { + let arg = unsafe { &*arg }; + for _ in 0..1000 { + print!("{}", arg.ch); + } + exit(arg.rc) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + let args = [ + Argument { ch: 'a', rc: 1 }, + Argument { ch: 'b', rc: 2 }, + Argument { ch: 'c', rc: 3 }, + ]; + for arg in args.iter() { + v.push(thread_create( + thread_print as usize, + arg as *const _ as usize, + )); + } + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + println!("threads with arg test passed!"); + 0 +} diff --git a/user/src/bin/ch8b_user_shell.rs b/user/src/bin/ch8b_user_shell.rs new file mode 100644 index 0000000..4288248 --- /dev/null +++ b/user/src/bin/ch8b_user_shell.rs @@ -0,0 +1,214 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +const DL: u8 = 0x7fu8; +const BS: u8 = 0x08u8; +const LINE_START: &str = ">> "; + +use alloc::string::String; +use alloc::vec::Vec; +use user_lib::console::getchar; +use user_lib::{close, dup, exec, fork, open, pipe, waitpid, OpenFlags}; + +#[derive(Debug)] +struct ProcessArguments { + input: String, + output: String, + args_copy: Vec, + args_addr: Vec<*const u8>, +} + +impl ProcessArguments { + pub fn new(command: &str) -> Self { + let args: Vec<_> = command.split(' ').collect(); + let mut args_copy: Vec = args + .iter() + .filter(|&arg| !arg.is_empty()) + .map(|&arg| { + let mut string = String::new(); + string.push_str(arg); + string.push('\0'); + string + }) + .collect(); + + // redirect input + let mut input = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == "<\0") + { + input = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + // redirect output + let mut output = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == ">\0") + { + output = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + let mut args_addr: Vec<*const u8> = args_copy.iter().map(|arg| arg.as_ptr()).collect(); + args_addr.push(core::ptr::null::()); + + Self { + input, + output, + args_copy, + args_addr, + } + } +} + +#[no_mangle] +pub fn main() -> i32 { + println!("Rust user shell"); + let mut line: String = String::new(); + print!("{}", LINE_START); + loop { + let c = getchar(); + match c { + LF | CR => { + println!(""); + if !line.is_empty() { + let splited: Vec<_> = line.as_str().split('|').collect(); + let process_arguments_list: Vec<_> = splited + .iter() + .map(|&cmd| ProcessArguments::new(cmd)) + .collect(); + let mut valid = true; + for (i, process_args) in process_arguments_list.iter().enumerate() { + if i == 0 { + if !process_args.output.is_empty() { + valid = false; + } + } else if i == process_arguments_list.len() - 1 { + if !process_args.input.is_empty() { + valid = false; + } + } else if !process_args.output.is_empty() || !process_args.input.is_empty() + { + valid = false; + } + } + if process_arguments_list.len() == 1 { + valid = true; + } + if !valid { + println!("Invalid command: Inputs/Outputs cannot be correctly binded!"); + } else { + // create pipes + let mut pipes_fd: Vec<[usize; 2]> = Vec::new(); + if !process_arguments_list.is_empty() { + for _ in 0..process_arguments_list.len() - 1 { + let mut pipe_fd = [0usize; 2]; + pipe(&mut pipe_fd); + pipes_fd.push(pipe_fd); + } + } + let mut children: Vec<_> = Vec::new(); + for (i, process_argument) in process_arguments_list.iter().enumerate() { + let pid = fork(); + if pid == 0 { + let input = &process_argument.input; + let output = &process_argument.output; + let args_copy = &process_argument.args_copy; + let args_addr = &process_argument.args_addr; + // redirect input + if !input.is_empty() { + let input_fd = open(input.as_str(), OpenFlags::RDONLY); + if input_fd == -1 { + println!("Error when opening file {}", input); + return -4; + } + let input_fd = input_fd as usize; + close(0); + assert_eq!(dup(input_fd), 0); + close(input_fd); + } + // redirect output + if !output.is_empty() { + let output_fd = open( + output.as_str(), + OpenFlags::CREATE | OpenFlags::WRONLY, + ); + if output_fd == -1 { + println!("Error when opening file {}", output); + return -4; + } + let output_fd = output_fd as usize; + close(1); + assert_eq!(dup(output_fd), 1); + close(output_fd); + } + // receive input from the previous process + if i > 0 { + close(0); + let read_end = pipes_fd.get(i - 1).unwrap()[0]; + assert_eq!(dup(read_end), 0); + } + // send output to the next process + if i < process_arguments_list.len() - 1 { + close(1); + let write_end = pipes_fd.get(i).unwrap()[1]; + assert_eq!(dup(write_end), 1); + } + // close all pipe ends inherited from the parent process + for pipe_fd in pipes_fd.iter() { + close(pipe_fd[0]); + close(pipe_fd[1]); + } + // execute new application + if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 { + println!("Error when executing!"); + return -4; + } + unreachable!(); + } else { + children.push(pid); + } + } + for pipe_fd in pipes_fd.iter() { + close(pipe_fd[0]); + close(pipe_fd[1]); + } + let mut exit_code: i32 = 0; + for pid in children.into_iter() { + let exit_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, exit_pid); + //println!("Shell: Process {} exited with code {}", pid, exit_code); + } + } + line.clear(); + } + print!("{}", LINE_START); + } + BS | DL => { + if !line.is_empty() { + print!("{}", BS as char); + print!(" "); + print!("{}", BS as char); + line.pop(); + } + } + _ => { + print!("{}", c as char); + line.push(c as char); + } + } + } +} diff --git a/user/src/console.rs b/user/src/console.rs new file mode 100644 index 0000000..6d6d1f3 --- /dev/null +++ b/user/src/console.rs @@ -0,0 +1,75 @@ +use alloc::collections::vec_deque::VecDeque; +use alloc::sync::Arc; +use core::fmt::{self, Write}; +use spin::mutex::Mutex; + +pub const STDIN: usize = 0; +pub const STDOUT: usize = 1; + +const CONSOLE_BUFFER_SIZE: usize = 256 * 10; + +use super::{read, write}; +use lazy_static::*; + +struct ConsoleBuffer(VecDeque); + +lazy_static! { + static ref CONSOLE_BUFFER: Arc> = { + let buffer = VecDeque::::with_capacity(CONSOLE_BUFFER_SIZE); + Arc::new(Mutex::new(ConsoleBuffer(buffer))) + }; +} + +impl ConsoleBuffer { + fn flush(&mut self) -> isize { + let s: &[u8] = self.0.make_contiguous(); + let ret = write(STDOUT, s); + self.0.clear(); + ret + } +} + +impl Write for ConsoleBuffer { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.as_bytes().iter() { + self.0.push_back(*c); + if (*c == b'\n' || self.0.len() == CONSOLE_BUFFER_SIZE) && -1 == self.flush() { + return Err(fmt::Error); + } + } + Ok(()) + } +} + +#[allow(unused)] +pub fn print(args: fmt::Arguments) { + let mut buf = CONSOLE_BUFFER.lock(); + // buf.write_fmt(args).unwrap(); + // BUG FIX: 关闭 stdout 后,本函数不能触发 panic,否则会造成死锁 + buf.write_fmt(args); +} + +#[macro_export] +macro_rules! print { + ($fmt: literal $(, $($arg: tt)+)?) => { + $crate::console::print(format_args!($fmt $(, $($arg)+)?)); + } +} + +#[macro_export] +macro_rules! println { + ($fmt: literal $(, $($arg: tt)+)?) => { + $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); + } +} + +pub fn getchar() -> u8 { + let mut c = [0u8; 1]; + read(STDIN, &mut c); + c[0] +} + +pub fn flush() { + let mut buf = CONSOLE_BUFFER.lock(); + buf.flush(); +} diff --git a/user/src/lang_items.rs b/user/src/lang_items.rs new file mode 100644 index 0000000..2d81a1d --- /dev/null +++ b/user/src/lang_items.rs @@ -0,0 +1,17 @@ +use crate::exit; + +#[panic_handler] +fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { + let err = panic_info.message().unwrap(); + if let Some(location) = panic_info.location() { + println!( + "Panicked at {}:{}, {}", + location.file(), + location.line(), + err + ); + } else { + println!("Panicked: {}", err); + } + exit(-1); +} diff --git a/user/src/lib.rs b/user/src/lib.rs new file mode 100644 index 0000000..cee97ee --- /dev/null +++ b/user/src/lib.rs @@ -0,0 +1,359 @@ +#![no_std] +#![feature(linkage)] +#![feature(panic_info_message)] +#![feature(alloc_error_handler)] + +#[macro_use] +pub mod console; +mod lang_items; +mod syscall; + +extern crate alloc; +extern crate core; +#[macro_use] +extern crate bitflags; + +use alloc::vec::Vec; +use buddy_system_allocator::LockedHeap; +pub use console::{flush, STDIN, STDOUT}; +pub use syscall::*; + +const USER_HEAP_SIZE: usize = 16384; + +static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE]; + +#[global_allocator] +static HEAP: LockedHeap = LockedHeap::empty(); + +#[alloc_error_handler] +pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { + panic!("Heap allocation error, layout = {:?}", layout); +} + +fn clear_bss() { + extern "C" { + fn start_bss(); + fn end_bss(); + } + unsafe { + core::slice::from_raw_parts_mut( + start_bss as usize as *mut u8, + end_bss as usize - start_bss as usize, + ) + .fill(0); + } +} + +#[no_mangle] +#[link_section = ".text.entry"] +pub extern "C" fn _start(argc: usize, argv: usize) -> ! { + clear_bss(); + unsafe { + HEAP.lock() + .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); + } + let mut v: Vec<&'static str> = Vec::new(); + for i in 0..argc { + let str_start = + unsafe { ((argv + i * core::mem::size_of::()) as *const usize).read_volatile() }; + let len = (0usize..) + .find(|i| unsafe { ((str_start + *i) as *const u8).read_volatile() == 0 }) + .unwrap(); + v.push( + core::str::from_utf8(unsafe { + core::slice::from_raw_parts(str_start as *const u8, len) + }) + .unwrap(), + ); + } + exit(main(argc, v.as_slice())); +} + +#[linkage = "weak"] +#[no_mangle] +fn main(_argc: usize, _argv: &[&str]) -> i32 { + panic!("Cannot find main!"); +} + +bitflags! { + pub struct OpenFlags: u32 { + const RDONLY = 0; + const WRONLY = 1 << 0; + const RDWR = 1 << 1; + const CREATE = 1 << 9; + const TRUNC = 1 << 10; + } +} + +#[repr(C)] +#[derive(Debug, Default)] +pub struct TimeVal { + pub sec: usize, + pub usec: usize, +} + +impl TimeVal { + pub fn new() -> Self { + Self::default() + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum TaskStatus { + UnInit, + Ready, + Running, + Exited, +} + +#[derive(Copy, Clone, Debug)] +pub struct SyscallInfo { + pub id: usize, + pub times: usize, +} + +const MAX_SYSCALL_NUM: usize = 500; + +#[derive(Debug)] +pub struct TaskInfo { + pub status: TaskStatus, + pub syscall_times: [u32; MAX_SYSCALL_NUM], + pub time: usize, +} + +impl TaskInfo { + pub fn new() -> Self { + TaskInfo { + status: TaskStatus::UnInit, + syscall_times: [0; MAX_SYSCALL_NUM], + time: 0, + } + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct Stat { + /// ID of device containing file + pub dev: u64, + /// inode number + pub ino: u64, + /// file type and mode + pub mode: StatMode, + /// number of hard links + pub nlink: u32, + /// unused pad + pad: [u64; 7], +} + +impl Stat { + pub fn new() -> Self { + Stat { + dev: 0, + ino: 0, + mode: StatMode::NULL, + nlink: 0, + pad: [0; 7], + } + } +} + +impl Default for Stat { + fn default() -> Self { + Self::new() + } +} + +bitflags! { + pub struct StatMode: u32 { + const NULL = 0; + /// directory + const DIR = 0o040000; + /// ordinary regular file + const FILE = 0o100000; + } +} + +const AT_FDCWD: isize = -100; + +pub fn open(path: &str, flags: OpenFlags) -> isize { + sys_openat(AT_FDCWD as usize, path, flags.bits, OpenFlags::RDWR.bits) +} + +pub fn close(fd: usize) -> isize { + if fd == STDOUT { + console::flush(); + } + sys_close(fd) +} + +pub fn read(fd: usize, buf: &mut [u8]) -> isize { + sys_read(fd, buf) +} + +pub fn write(fd: usize, buf: &[u8]) -> isize { + sys_write(fd, buf) +} + +pub fn link(old_path: &str, new_path: &str) -> isize { + sys_linkat(AT_FDCWD as usize, old_path, AT_FDCWD as usize, new_path, 0) +} + +pub fn unlink(path: &str) -> isize { + sys_unlinkat(AT_FDCWD as usize, path, 0) +} + +pub fn fstat(fd: usize, st: &Stat) -> isize { + sys_fstat(fd, st) +} + +pub fn mail_read(buf: &mut [u8]) -> isize { + sys_mail_read(buf) +} + +pub fn mail_write(pid: usize, buf: &[u8]) -> isize { + sys_mail_write(pid, buf) +} + +pub fn exit(exit_code: i32) -> ! { + console::flush(); + sys_exit(exit_code); +} + +pub fn yield_() -> isize { + sys_yield() +} + +pub fn get_time() -> isize { + let time = TimeVal::new(); + match sys_get_time(&time, 0) { + 0 => ((time.sec & 0xffff) * 1000 + time.usec / 1000) as isize, + _ => -1, + } +} + +pub fn getpid() -> isize { + sys_getpid() +} + +pub fn fork() -> isize { + sys_fork() +} + +pub fn exec(path: &str, args: &[*const u8]) -> isize { + sys_exec(path, args) +} + +pub fn set_priority(prio: isize) -> isize { + sys_set_priority(prio) +} + +pub fn wait(exit_code: &mut i32) -> isize { + loop { + match sys_waitpid(-1, exit_code as *mut _) { + -2 => { + sys_yield(); + } + n => { + return n; + } + } + } +} + +pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { + loop { + match sys_waitpid(pid as isize, exit_code as *mut _) { + -2 => { + sys_yield(); + } + n => { + return n; + } + } + } +} + +pub fn sleep_blocking(sleep_ms: usize) { + sys_sleep(sleep_ms); +} + +pub fn sleep(period_ms: usize) { + let start = get_time(); + while get_time() < start + period_ms as isize { + sys_yield(); + } +} +pub fn mmap(start: usize, len: usize, prot: usize) -> isize { + sys_mmap(start, len, prot) +} + +pub fn munmap(start: usize, len: usize) -> isize { + sys_munmap(start, len) +} + +pub fn spawn(path: &str) -> isize { + sys_spawn(path) +} + +pub fn dup(fd: usize) -> isize { + sys_dup(fd) +} +pub fn pipe(pipe_fd: &mut [usize]) -> isize { + sys_pipe(pipe_fd) +} + +pub fn task_info(info: &TaskInfo) -> isize { + sys_task_info(info) +} + +pub fn thread_create(entry: usize, arg: usize) -> isize { + sys_thread_create(entry, arg) +} +pub fn gettid() -> isize { + sys_gettid() +} +pub fn waittid(tid: usize) -> isize { + loop { + match sys_waittid(tid) { + -2 => { + yield_(); + } + exit_code => return exit_code, + } + } +} + +pub fn mutex_create() -> isize { + sys_mutex_create(false) +} +pub fn mutex_blocking_create() -> isize { + sys_mutex_create(true) +} +pub fn mutex_lock(mutex_id: usize) -> isize { + sys_mutex_lock(mutex_id) +} +pub fn mutex_unlock(mutex_id: usize) { + sys_mutex_unlock(mutex_id); +} +pub fn semaphore_create(res_count: usize) -> isize { + sys_semaphore_create(res_count) +} +pub fn semaphore_up(sem_id: usize) { + sys_semaphore_up(sem_id); +} +pub fn enable_deadlock_detect(enabled: bool) -> isize { + sys_enable_deadlock_detect(enabled as usize) +} +pub fn semaphore_down(sem_id: usize) -> isize { + sys_semaphore_down(sem_id) +} +pub fn condvar_create() -> isize { + sys_condvar_create(0) +} +pub fn condvar_signal(condvar_id: usize) { + sys_condvar_signal(condvar_id); +} +pub fn condvar_wait(condvar_id: usize, mutex_id: usize) { + sys_condvar_wait(condvar_id, mutex_id); +} diff --git a/user/src/linker.ld b/user/src/linker.ld new file mode 100644 index 0000000..f3f996e --- /dev/null +++ b/user/src/linker.ld @@ -0,0 +1,33 @@ +OUTPUT_ARCH(riscv) +ENTRY(_start) + +BASE_ADDRESS = 0x0; + +SECTIONS +{ + . = BASE_ADDRESS; + .text : { + *(.text.entry) + *(.text .text.*) + } + . = ALIGN(4K); + .rodata : { + *(.rodata .rodata.*) + *(.srodata .srodata.*) + } + . = ALIGN(4K); + .data : { + *(.data .data.*) + *(.sdata .sdata.*) + } + .bss : { + start_bss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + end_bss = .; + } + /DISCARD/ : { + *(.eh_frame) + *(.debug*) + } +} \ No newline at end of file diff --git a/user/src/syscall.rs b/user/src/syscall.rs new file mode 100644 index 0000000..f671438 --- /dev/null +++ b/user/src/syscall.rs @@ -0,0 +1,258 @@ +use crate::TaskInfo; + +use super::{Stat, TimeVal}; + +pub const SYSCALL_OPENAT: usize = 56; +pub const SYSCALL_CLOSE: usize = 57; +pub const SYSCALL_READ: usize = 63; +pub const SYSCALL_WRITE: usize = 64; +pub const SYSCALL_UNLINKAT: usize = 35; +pub const SYSCALL_LINKAT: usize = 37; +pub const SYSCALL_FSTAT: usize = 80; +pub const SYSCALL_EXIT: usize = 93; +pub const SYSCALL_SLEEP: usize = 101; +pub const SYSCALL_YIELD: usize = 124; +pub const SYSCALL_GETTIMEOFDAY: usize = 169; +pub const SYSCALL_GETPID: usize = 172; +pub const SYSCALL_GETTID: usize = 178; +pub const SYSCALL_FORK: usize = 220; +pub const SYSCALL_EXEC: usize = 221; +pub const SYSCALL_WAITPID: usize = 260; +pub const SYSCALL_SET_PRIORITY: usize = 140; +pub const SYSCALL_MUNMAP: usize = 215; +pub const SYSCALL_MMAP: usize = 222; +pub const SYSCALL_SPAWN: usize = 400; +pub const SYSCALL_MAIL_READ: usize = 401; +pub const SYSCALL_MAIL_WRITE: usize = 402; +pub const SYSCALL_DUP: usize = 24; +pub const SYSCALL_PIPE: usize = 59; +pub const SYSCALL_TASK_INFO: usize = 410; +pub const SYSCALL_THREAD_CREATE: usize = 460; +pub const SYSCALL_WAITTID: usize = 462; +pub const SYSCALL_MUTEX_CREATE: usize = 463; +pub const SYSCALL_MUTEX_LOCK: usize = 464; +pub const SYSCALL_MUTEX_UNLOCK: usize = 466; +pub const SYSCALL_SEMAPHORE_CREATE: usize = 467; +pub const SYSCALL_SEMAPHORE_UP: usize = 468; +pub const SYSCALL_ENABLE_DEADLOCK_DETECT: usize = 469; +pub const SYSCALL_SEMAPHORE_DOWN: usize = 470; +pub const SYSCALL_CONDVAR_CREATE: usize = 471; +pub const SYSCALL_CONDVAR_SIGNAL: usize = 472; +pub const SYSCALL_CONDVAR_WAIT: usize = 473; + +pub fn syscall(id: usize, args: [usize; 3]) -> isize { + let mut ret: isize; + unsafe { + core::arch::asm!( + "ecall", + inlateout("x10") args[0] => ret, + in("x11") args[1], + in("x12") args[2], + in("x17") id + ); + } + ret +} + +pub fn syscall6(id: usize, args: [usize; 6]) -> isize { + let mut ret: isize; + unsafe { + core::arch::asm!("ecall", + inlateout("x10") args[0] => ret, + in("x11") args[1], + in("x12") args[2], + in("x13") args[3], + in("x14") args[4], + in("x15") args[5], + in("x17") id + ); + } + ret +} + +pub fn sys_openat(dirfd: usize, path: &str, flags: u32, mode: u32) -> isize { + syscall6( + SYSCALL_OPENAT, + [ + dirfd, + path.as_ptr() as usize, + flags as usize, + mode as usize, + 0, + 0, + ], + ) +} + +pub fn sys_close(fd: usize) -> isize { + syscall(SYSCALL_CLOSE, [fd, 0, 0]) +} + +pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize { + syscall( + SYSCALL_READ, + [fd, buffer.as_mut_ptr() as usize, buffer.len()], + ) +} + +pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { + syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) +} + +pub fn sys_linkat( + old_dirfd: usize, + old_path: &str, + new_dirfd: usize, + new_path: &str, + flags: usize, +) -> isize { + syscall6( + SYSCALL_LINKAT, + [ + old_dirfd, + old_path.as_ptr() as usize, + new_dirfd, + new_path.as_ptr() as usize, + flags, + 0, + ], + ) +} + +pub fn sys_unlinkat(dirfd: usize, path: &str, flags: usize) -> isize { + syscall(SYSCALL_UNLINKAT, [dirfd, path.as_ptr() as usize, flags]) +} + +pub fn sys_fstat(fd: usize, st: &Stat) -> isize { + syscall(SYSCALL_FSTAT, [fd, st as *const _ as usize, 0]) +} + +pub fn sys_mail_read(buffer: &mut [u8]) -> isize { + syscall( + SYSCALL_MAIL_READ, + [buffer.as_ptr() as usize, buffer.len(), 0], + ) +} + +pub fn sys_mail_write(pid: usize, buffer: &[u8]) -> isize { + syscall( + SYSCALL_MAIL_WRITE, + [pid, buffer.as_ptr() as usize, buffer.len()], + ) +} + +pub fn sys_exit(exit_code: i32) -> ! { + syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]); + panic!("sys_exit never returns!"); +} + +pub fn sys_sleep(sleep_ms: usize) -> isize { + syscall(SYSCALL_SLEEP, [sleep_ms, 0, 0]) +} + +pub fn sys_yield() -> isize { + syscall(SYSCALL_YIELD, [0, 0, 0]) +} + +pub fn sys_get_time(time: &TimeVal, tz: usize) -> isize { + syscall(SYSCALL_GETTIMEOFDAY, [time as *const _ as usize, tz, 0]) +} + +pub fn sys_getpid() -> isize { + syscall(SYSCALL_GETPID, [0, 0, 0]) +} + +pub fn sys_fork() -> isize { + syscall(SYSCALL_FORK, [0, 0, 0]) +} + +pub fn sys_exec(path: &str, args: &[*const u8]) -> isize { + syscall( + SYSCALL_EXEC, + [path.as_ptr() as usize, args.as_ptr() as usize, 0], + ) +} + +pub fn sys_waitpid(pid: isize, xstatus: *mut i32) -> isize { + syscall(SYSCALL_WAITPID, [pid as usize, xstatus as usize, 0]) +} + +pub fn sys_set_priority(prio: isize) -> isize { + syscall(SYSCALL_SET_PRIORITY, [prio as usize, 0, 0]) +} + +pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize { + syscall(SYSCALL_MMAP, [start, len, prot]) +} + +pub fn sys_munmap(start: usize, len: usize) -> isize { + syscall(SYSCALL_MUNMAP, [start, len, 0]) +} + +pub fn sys_spawn(path: &str) -> isize { + syscall(SYSCALL_SPAWN, [path.as_ptr() as usize, 0, 0]) +} + +pub fn sys_dup(fd: usize) -> isize { + syscall(SYSCALL_DUP, [fd, 0, 0]) +} + +pub fn sys_pipe(pipe: &mut [usize]) -> isize { + syscall(SYSCALL_PIPE, [pipe.as_mut_ptr() as usize, 0, 0]) +} + +pub fn sys_task_info(info: &TaskInfo) -> isize { + syscall(SYSCALL_TASK_INFO, [info as *const _ as usize, 0, 0]) +} + +pub fn sys_thread_create(entry: usize, arg: usize) -> isize { + syscall(SYSCALL_THREAD_CREATE, [entry, arg, 0]) +} + +pub fn sys_gettid() -> isize { + syscall(SYSCALL_GETTID, [0; 3]) +} + +pub fn sys_waittid(tid: usize) -> isize { + syscall(SYSCALL_WAITTID, [tid, 0, 0]) +} + +pub fn sys_mutex_create(blocking: bool) -> isize { + syscall(SYSCALL_MUTEX_CREATE, [blocking as usize, 0, 0]) +} + +pub fn sys_mutex_lock(id: usize) -> isize { + syscall(SYSCALL_MUTEX_LOCK, [id, 0, 0]) +} + +pub fn sys_mutex_unlock(id: usize) -> isize { + syscall(SYSCALL_MUTEX_UNLOCK, [id, 0, 0]) +} + +pub fn sys_semaphore_create(res_count: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_CREATE, [res_count, 0, 0]) +} + +pub fn sys_semaphore_up(sem_id: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_UP, [sem_id, 0, 0]) +} + +pub fn sys_enable_deadlock_detect(enabled: usize) -> isize { + syscall(SYSCALL_ENABLE_DEADLOCK_DETECT, [enabled, 0, 0]) +} + +pub fn sys_semaphore_down(sem_id: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_DOWN, [sem_id, 0, 0]) +} + +pub fn sys_condvar_create(_arg: usize) -> isize { + syscall(SYSCALL_CONDVAR_CREATE, [_arg, 0, 0]) +} + +pub fn sys_condvar_signal(condvar_id: usize) -> isize { + syscall(SYSCALL_CONDVAR_SIGNAL, [condvar_id, 0, 0]) +} + +pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { + syscall(SYSCALL_CONDVAR_WAIT, [condvar_id, mutex_id, 0]) +}