mirror of
https://github.com/LearningOS/rust-based-os-comp2022.git
synced 2026-02-07 20:34:08 +08:00
add test apps
This commit is contained in:
7
user/.cargo/config
Normal file
7
user/.cargo/config
Normal file
@@ -0,0 +1,7 @@
|
||||
[build]
|
||||
target = "riscv64gc-unknown-none-elf"
|
||||
|
||||
[target.riscv64gc-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-Clink-args=-Tsrc/linker.ld",
|
||||
]
|
||||
4
user/.gitignore
vendored
Normal file
4
user/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
target/*
|
||||
.vscode/
|
||||
build/
|
||||
.idea/
|
||||
19
user/Cargo.toml
Normal file
19
user/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "user_lib"
|
||||
version = "0.1.0"
|
||||
authors = ["Yifan Wu <shinbokuow@163.com>"]
|
||||
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
|
||||
64
user/Makefile
Normal file
64
user/Makefile
Normal file
@@ -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
|
||||
23
user/build.py
Normal file
23
user/build.py
Normal file
@@ -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
|
||||
16
user/src/bin/ch2b_bad_address.rs
Normal file
16
user/src/bin/ch2b_bad_address.rs
Normal file
@@ -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");
|
||||
}
|
||||
15
user/src/bin/ch2b_bad_instructions.rs
Normal file
15
user/src/bin/ch2b_bad_instructions.rs
Normal file
@@ -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");
|
||||
}
|
||||
16
user/src/bin/ch2b_bad_register.rs
Normal file
16
user/src/bin/ch2b_bad_register.rs
Normal file
@@ -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);
|
||||
}
|
||||
14
user/src/bin/ch2b_hello_world.rs
Normal file
14
user/src/bin/ch2b_hello_world.rs
Normal file
@@ -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
|
||||
}
|
||||
28
user/src/bin/ch2b_power_3.rs
Normal file
28
user/src/bin/ch2b_power_3.rs
Normal file
@@ -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
|
||||
}
|
||||
28
user/src/bin/ch2b_power_5.rs
Normal file
28
user/src/bin/ch2b_power_5.rs
Normal file
@@ -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
|
||||
}
|
||||
28
user/src/bin/ch2b_power_7.rs
Normal file
28
user/src/bin/ch2b_power_7.rs
Normal file
@@ -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
|
||||
}
|
||||
46
user/src/bin/ch3_taskinfo.rs
Normal file
46
user/src/bin/ch3_taskinfo.rs
Normal file
@@ -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
|
||||
}
|
||||
24
user/src/bin/ch3b_sleep.rs
Normal file
24
user/src/bin/ch3b_sleep.rs
Normal file
@@ -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
|
||||
}
|
||||
22
user/src/bin/ch3b_sleep1.rs
Normal file
22
user/src/bin/ch3b_sleep1.rs
Normal file
@@ -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
|
||||
}
|
||||
30
user/src/bin/ch3b_yield0.rs
Normal file
30
user/src/bin/ch3b_yield0.rs
Normal file
@@ -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
|
||||
}
|
||||
30
user/src/bin/ch3b_yield1.rs
Normal file
30
user/src/bin/ch3b_yield1.rs
Normal file
@@ -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
|
||||
}
|
||||
30
user/src/bin/ch3b_yield2.rs
Normal file
30
user/src/bin/ch3b_yield2.rs
Normal file
@@ -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
|
||||
}
|
||||
33
user/src/bin/ch4_mmap0.rs
Normal file
33
user/src/bin/ch4_mmap0.rs
Normal file
@@ -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
|
||||
}
|
||||
25
user/src/bin/ch4_mmap1.rs
Normal file
25
user/src/bin/ch4_mmap1.rs
Normal file
@@ -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
|
||||
}
|
||||
26
user/src/bin/ch4_mmap2.rs
Normal file
26
user/src/bin/ch4_mmap2.rs
Normal file
@@ -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
|
||||
}
|
||||
25
user/src/bin/ch4_mmap3.rs
Normal file
25
user/src/bin/ch4_mmap3.rs
Normal file
@@ -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
|
||||
}
|
||||
36
user/src/bin/ch4_unmap.rs
Normal file
36
user/src/bin/ch4_unmap.rs
Normal file
@@ -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
|
||||
}
|
||||
23
user/src/bin/ch4_unmap2.rs
Normal file
23
user/src/bin/ch4_unmap2.rs
Normal file
@@ -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
|
||||
}
|
||||
17
user/src/bin/ch5_exit0.rs
Normal file
17
user/src/bin/ch5_exit0.rs
Normal file
@@ -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
|
||||
}
|
||||
17
user/src/bin/ch5_exit1.rs
Normal file
17
user/src/bin/ch5_exit1.rs
Normal file
@@ -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
|
||||
}
|
||||
18
user/src/bin/ch5_getpid.rs
Normal file
18
user/src/bin/ch5_getpid.rs
Normal file
@@ -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
|
||||
}
|
||||
20
user/src/bin/ch5_setprio.rs
Normal file
20
user/src/bin/ch5_setprio.rs
Normal file
@@ -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
|
||||
}
|
||||
29
user/src/bin/ch5_spawn0.rs
Normal file
29
user/src/bin/ch5_spawn0.rs
Normal file
@@ -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
|
||||
}
|
||||
35
user/src/bin/ch5_spawn1.rs
Normal file
35
user/src/bin/ch5_spawn1.rs
Normal file
@@ -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
|
||||
}
|
||||
33
user/src/bin/ch5_stride.rs
Normal file
33
user/src/bin/ch5_stride.rs
Normal file
@@ -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
|
||||
}
|
||||
43
user/src/bin/ch5_stride0.rs
Normal file
43
user/src/bin/ch5_stride0.rs
Normal file
@@ -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
|
||||
}
|
||||
39
user/src/bin/ch5_stride1.rs
Normal file
39
user/src/bin/ch5_stride1.rs
Normal file
@@ -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
|
||||
}
|
||||
39
user/src/bin/ch5_stride2.rs
Normal file
39
user/src/bin/ch5_stride2.rs
Normal file
@@ -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
|
||||
}
|
||||
39
user/src/bin/ch5_stride3.rs
Normal file
39
user/src/bin/ch5_stride3.rs
Normal file
@@ -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
|
||||
}
|
||||
39
user/src/bin/ch5_stride4.rs
Normal file
39
user/src/bin/ch5_stride4.rs
Normal file
@@ -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
|
||||
}
|
||||
39
user/src/bin/ch5_stride5.rs
Normal file
39
user/src/bin/ch5_stride5.rs
Normal file
@@ -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
|
||||
}
|
||||
61
user/src/bin/ch5_usertest.rs
Normal file
61
user/src/bin/ch5_usertest.rs
Normal file
@@ -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
|
||||
}
|
||||
30
user/src/bin/ch5b_exit.rs
Normal file
30
user/src/bin/ch5b_exit.rs
Normal file
@@ -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
|
||||
}
|
||||
34
user/src/bin/ch5b_forktest.rs
Normal file
34
user/src/bin/ch5b_forktest.rs
Normal file
@@ -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
|
||||
}
|
||||
34
user/src/bin/ch5b_forktest2.rs
Normal file
34
user/src/bin/ch5b_forktest2.rs
Normal file
@@ -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
|
||||
}
|
||||
28
user/src/bin/ch5b_forktest_simple.rs
Normal file
28
user/src/bin/ch5b_forktest_simple.rs
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
37
user/src/bin/ch5b_forktree.rs
Normal file
37
user/src/bin/ch5b_forktree.rs
Normal file
@@ -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
|
||||
}
|
||||
28
user/src/bin/ch5b_initproc.rs
Normal file
28
user/src/bin/ch5b_initproc.rs
Normal file
@@ -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
|
||||
}
|
||||
66
user/src/bin/ch5b_user_shell.rs
Normal file
66
user/src/bin/ch5b_user_shell.rs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
user/src/bin/ch6_file0.rs
Normal file
31
user/src/bin/ch6_file0.rs
Normal file
@@ -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
|
||||
}
|
||||
26
user/src/bin/ch6_file1.rs
Normal file
26
user/src/bin/ch6_file1.rs
Normal file
@@ -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
|
||||
}
|
||||
46
user/src/bin/ch6_file2.rs
Normal file
46
user/src/bin/ch6_file2.rs
Normal file
@@ -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
|
||||
}
|
||||
31
user/src/bin/ch6_file3.rs
Normal file
31
user/src/bin/ch6_file3.rs
Normal file
@@ -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
|
||||
}
|
||||
51
user/src/bin/ch6_usertest.rs
Normal file
51
user/src/bin/ch6_usertest.rs
Normal file
@@ -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
|
||||
}
|
||||
33
user/src/bin/ch6b_cat.rs
Normal file
33
user/src/bin/ch6b_cat.rs
Normal file
@@ -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
|
||||
}
|
||||
38
user/src/bin/ch6b_filetest_simple.rs
Normal file
38
user/src/bin/ch6b_filetest_simple.rs
Normal file
@@ -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
|
||||
}
|
||||
28
user/src/bin/ch6b_initproc.rs
Normal file
28
user/src/bin/ch6b_initproc.rs
Normal file
@@ -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
|
||||
}
|
||||
66
user/src/bin/ch6b_user_shell.rs
Normal file
66
user/src/bin/ch6b_user_shell.rs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
user/src/bin/ch7_usertest.rs
Normal file
29
user/src/bin/ch7_usertest.rs
Normal file
@@ -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
|
||||
}
|
||||
34
user/src/bin/ch7b_cat.rs
Normal file
34
user/src/bin/ch7b_cat.rs
Normal file
@@ -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
|
||||
}
|
||||
28
user/src/bin/ch7b_initproc.rs
Normal file
28
user/src/bin/ch7b_initproc.rs
Normal file
@@ -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
|
||||
}
|
||||
71
user/src/bin/ch7b_pipe_large_test.rs
Normal file
71
user/src/bin/ch7b_pipe_large_test.rs
Normal file
@@ -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::<usize>();
|
||||
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::<usize>();
|
||||
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::<usize>(core::str::from_utf8(&child_result[..result_len]).unwrap())
|
||||
.unwrap()
|
||||
);
|
||||
let mut _unused: i32 = 0;
|
||||
wait(&mut _unused);
|
||||
println!("pipe_large_test passed!");
|
||||
0
|
||||
}
|
||||
}
|
||||
44
user/src/bin/ch7b_pipetest.rs
Normal file
44
user/src/bin/ch7b_pipetest.rs
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
21
user/src/bin/ch7b_run_pipe_test.rs
Normal file
21
user/src/bin/ch7b_run_pipe_test.rs
Normal file
@@ -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
|
||||
}
|
||||
130
user/src/bin/ch7b_user_shell.rs
Normal file
130
user/src/bin/ch7b_user_shell.rs
Normal file
@@ -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<String> = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
user/src/bin/ch7b_yield.rs
Normal file
17
user/src/bin/ch7b_yield.rs
Normal file
@@ -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
|
||||
}
|
||||
22
user/src/bin/ch8_deadlock_mutex1.rs
Normal file
22
user/src/bin/ch8_deadlock_mutex1.rs
Normal file
@@ -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
|
||||
}
|
||||
102
user/src/bin/ch8_deadlock_sem1.rs
Normal file
102
user/src/bin/ch8_deadlock_sem1.rs
Normal file
@@ -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<usize>; 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
|
||||
}
|
||||
76
user/src/bin/ch8_deadlock_sem2.rs
Normal file
76
user/src/bin/ch8_deadlock_sem2.rs
Normal file
@@ -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<usize>; 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
|
||||
}
|
||||
60
user/src/bin/ch8_usertest.rs
Normal file
60
user/src/bin/ch8_usertest.rs
Normal file
@@ -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::<u8>()]);
|
||||
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
|
||||
}
|
||||
28
user/src/bin/ch8b_initproc.rs
Normal file
28
user/src/bin/ch8b_initproc.rs
Normal file
@@ -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::<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
|
||||
}
|
||||
73
user/src/bin/ch8b_mpsc_sem.rs
Normal file
73
user/src/bin/ch8b_mpsc_sem.rs
Normal file
@@ -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
|
||||
}
|
||||
109
user/src/bin/ch8b_phil_din_mutex.rs
Normal file
109
user/src/bin/ch8b_phil_din_mutex.rs
Normal file
@@ -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
|
||||
}
|
||||
43
user/src/bin/ch8b_race_adder.rs
Normal file
43
user/src/bin/ch8b_race_adder.rs
Normal file
@@ -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
|
||||
}
|
||||
52
user/src/bin/ch8b_race_adder_atomic.rs
Normal file
52
user/src/bin/ch8b_race_adder_atomic.rs
Normal file
@@ -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
|
||||
}
|
||||
52
user/src/bin/ch8b_race_adder_loop.rs
Normal file
52
user/src/bin/ch8b_race_adder_loop.rs
Normal file
@@ -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
|
||||
}
|
||||
47
user/src/bin/ch8b_race_adder_mutex_spin.rs
Normal file
47
user/src/bin/ch8b_race_adder_mutex_spin.rs
Normal file
@@ -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
|
||||
}
|
||||
346
user/src/bin/ch8b_stackful_coroutine.rs
Normal file
346
user/src/bin/ch8b_stackful_coroutine.rs
Normal file
@@ -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<Task>,
|
||||
current: usize,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum State {
|
||||
Available,
|
||||
Running,
|
||||
Ready,
|
||||
}
|
||||
|
||||
struct Task {
|
||||
id: usize,
|
||||
stack: Vec<u8>,
|
||||
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<Task> = (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);
|
||||
}
|
||||
129
user/src/bin/ch8b_stackless_coroutine.rs
Normal file
129
user/src/bin/ch8b_stackless_coroutine.rs
Normal file
@@ -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<Self::Output> {
|
||||
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<Pin<Box<dyn Future<Output = ()>>>>,
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
fn new() -> Self {
|
||||
Executor {
|
||||
tasks: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push<C, F>(&mut self, closure: C)
|
||||
where
|
||||
F: Future<Output = ()> + '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
|
||||
}
|
||||
45
user/src/bin/ch8b_sync_sem.rs
Normal file
45
user/src/bin/ch8b_sync_sem.rs
Normal file
@@ -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
|
||||
}
|
||||
59
user/src/bin/ch8b_test_condvar.rs
Normal file
59
user/src/bin/ch8b_test_condvar.rs
Normal file
@@ -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
|
||||
}
|
||||
62
user/src/bin/ch8b_threads.rs
Normal file
62
user/src/bin/ch8b_threads.rs
Normal file
@@ -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
|
||||
}
|
||||
45
user/src/bin/ch8b_threads_arg.rs
Normal file
45
user/src/bin/ch8b_threads_arg.rs
Normal file
@@ -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
|
||||
}
|
||||
214
user/src/bin/ch8b_user_shell.rs
Normal file
214
user/src/bin/ch8b_user_shell.rs
Normal file
@@ -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<String>,
|
||||
args_addr: Vec<*const u8>,
|
||||
}
|
||||
|
||||
impl ProcessArguments {
|
||||
pub fn new(command: &str) -> Self {
|
||||
let args: Vec<_> = command.split(' ').collect();
|
||||
let mut args_copy: Vec<String> = 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::<u8>());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
user/src/console.rs
Normal file
75
user/src/console.rs
Normal file
@@ -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<u8>);
|
||||
|
||||
lazy_static! {
|
||||
static ref CONSOLE_BUFFER: Arc<Mutex<ConsoleBuffer>> = {
|
||||
let buffer = VecDeque::<u8>::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();
|
||||
}
|
||||
17
user/src/lang_items.rs
Normal file
17
user/src/lang_items.rs
Normal file
@@ -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);
|
||||
}
|
||||
359
user/src/lib.rs
Normal file
359
user/src/lib.rs
Normal file
@@ -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::<usize>()) 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);
|
||||
}
|
||||
33
user/src/linker.ld
Normal file
33
user/src/linker.ld
Normal file
@@ -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*)
|
||||
}
|
||||
}
|
||||
258
user/src/syscall.rs
Normal file
258
user/src/syscall.rs
Normal file
@@ -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])
|
||||
}
|
||||
Reference in New Issue
Block a user