Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
|
7787be0175 | 4 years ago |
|
f44881b1f5 | 4 years ago |
|
8cf293cb6c | 4 years ago |
|
940e88a002 | 4 years ago |
|
bb98f7f88c | 4 years ago |
|
7eda37a407 | 4 years ago |
@ -1,16 +1,67 @@
|
||||
# rCore-Tutorial-v3
|
||||
rCore-Tutorial version 3.x
|
||||
rCore-Tutorial version 3.5. See the [Documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/).
|
||||
|
||||
## Dependency
|
||||
## news
|
||||
- 2021.11.20: Now we are updating our labs. Please checkout chX-dev Branches for our current new labs. (Notice: please see the [Dependency] section in the end of this doc)
|
||||
|
||||
### Binaries
|
||||
## Overview
|
||||
|
||||
* rustc 1.56.0-nightly (08095fc1f 2021-07-26)
|
||||
This project aims to show how to write an **Unix-like OS** running on **RISC-V** platforms **from scratch** in **[Rust](https://www.rust-lang.org/)** for **beginners** without any background knowledge about **computer architectures, assembly languages or operating systems**.
|
||||
|
||||
* qemu: 5.0.0
|
||||
## Features
|
||||
|
||||
* rustsbi-lib: 0.2.0-alpha.4
|
||||
* Platform supported: `qemu-system-riscv64` simulator or dev boards based on [Kendryte K210 SoC](https://canaan.io/product/kendryteai) such as [Maix Dock](https://www.seeedstudio.com/Sipeed-MAIX-Dock-p-4815.html)
|
||||
* OS
|
||||
* concurrency of multiple processes
|
||||
* preemptive scheduling(Round-Robin algorithm)
|
||||
* dynamic memory management in kernel
|
||||
* virtual memory
|
||||
* a simple file system with a block cache
|
||||
* an interactive shell in the userspace
|
||||
* **only 4K+ LoC**
|
||||
* [A detailed documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/) in spite of the lack of comments in the code(English version is not available at present)
|
||||
|
||||
rustsbi-qemu: d4968dd2
|
||||
## Run our project
|
||||
|
||||
rustsbi-k210: b689314e
|
||||
TODO:
|
||||
|
||||
## Working in progress
|
||||
|
||||
Now we are still updating our project, you can find latest changes on branches `chX-dev` such as `ch1-dev`. We are intended to publish first release 3.5.0 after completing most of the tasks mentioned below.
|
||||
|
||||
Overall progress: ch7
|
||||
|
||||
### Completed
|
||||
|
||||
* [x] automatically clean up and rebuild before running our project on a different platform
|
||||
* [x] fix `power` series application in early chapters, now you can find modulus in the output
|
||||
* [x] use `UPSafeCell` instead of `RefCell` or `spin::Mutex` in order to access static data structures and adjust its API so that it cannot be borrowed twice at a time(mention `& .exclusive_access().task[0]` in `run_first_task`)
|
||||
* [x] move `TaskContext` into `TaskControlBlock` instead of restoring it in place on kernel stack(since ch3), eliminating annoying `task_cx_ptr2`
|
||||
* [x] replace `llvm_asm!` with `asm!`
|
||||
* [x] expand the fs image size generated by `rcore-fs-fuse` to 128MiB
|
||||
* [x] add a new test named `huge_write` which evaluates the fs performance(qemu\~500KiB/s k210\~50KiB/s)
|
||||
* [x] flush all block cache to disk after a fs transaction which involves write operation
|
||||
* [x] replace `spin::Mutex` with `UPSafeCell` before SMP chapter
|
||||
* [x] add codes for a new chapter about synchronization & mutual exclusion(uniprocessor only)
|
||||
|
||||
### Todo(High priority)
|
||||
|
||||
* [ ] support Allwinner's RISC-V D1 chip
|
||||
* [ ] bug fix: we should call `find_pte` rather than `find_pte_create` in `PageTable::unmap`
|
||||
* [ ] bug fix: check validity of level-3 pte in `find_pte` instead of checking it outside this function
|
||||
* [ ] use old fs image optionally, do not always rebuild the image
|
||||
* [ ] add new system calls: getdents64/fstat
|
||||
* [ ] shell functionality improvement(to be continued...)
|
||||
* [ ] give every non-zero process exit code an unique and clear error type
|
||||
* [ ] effective error handling of mm module
|
||||
|
||||
### Todo(Low priority)
|
||||
|
||||
* [ ] rewrite practice doc and remove some inproper questions
|
||||
* [ ] provide smooth debug experience at a Rust source code level
|
||||
* [ ] format the code using official tools
|
||||
|
||||
|
||||
### Crates
|
||||
|
||||
We will add them later.
|
||||
|
@ -0,0 +1,18 @@
|
||||
# rCore-Tutorial-v3
|
||||
rCore-Tutorial version 3.x
|
||||
|
||||
## Dependency
|
||||
|
||||
### Binaries
|
||||
|
||||
* rustc: 1.57.0-nightly (e1e9319d9 2021-10-14)
|
||||
|
||||
* cargo-binutils: 0.3.3
|
||||
|
||||
* qemu: 5.0.0
|
||||
|
||||
* rustsbi-lib: 0.2.0-alpha.4
|
||||
|
||||
rustsbi-qemu: d4968dd2
|
||||
|
||||
rustsbi-k210: b689314e
|
@ -1 +1 @@
|
||||
nightly-2021-08-25
|
||||
nightly-2021-10-15
|
||||
|
@ -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
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{getpid, mail_read, mail_write};
|
||||
|
||||
const BUF_LEN: usize = 256;
|
||||
const MAIL_MAX: usize = 16;
|
||||
|
||||
/// 测试邮箱容量,输出 mail1 test OK! 就算正确。
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let pid = getpid();
|
||||
let buffer0 = ['a' as u8; BUF_LEN];
|
||||
for _ in 0..MAIL_MAX {
|
||||
assert_eq!(mail_write(pid as usize, &buffer0), BUF_LEN as isize);
|
||||
}
|
||||
assert_eq!(mail_write(pid as usize, &buffer0), -1);
|
||||
let mut buf = [0u8; BUF_LEN];
|
||||
assert_eq!(mail_read(&mut buf), BUF_LEN as isize);
|
||||
assert_eq!(mail_write(pid as usize, &buffer0), BUF_LEN as isize);
|
||||
assert_eq!(mail_write(pid as usize, &buffer0), -1);
|
||||
println!("mail1 test OK!");
|
||||
0
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{exit, fork, mail_read, mail_write, sleep, wait};
|
||||
|
||||
const BUF_LEN: usize = 256;
|
||||
|
||||
// 双进程邮箱测试,最终输出 mail2 test OK! 就算正确。
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
println!("I am child");
|
||||
let mut buffer = [0u8; BUF_LEN];
|
||||
assert_eq!(mail_read(&mut buffer), -1);
|
||||
println!("child read 1 mail fail");
|
||||
println!("child sleep 2s");
|
||||
sleep(2000 as usize);
|
||||
for i in 0..16 {
|
||||
let mut buffer = [0u8; BUF_LEN];
|
||||
assert_eq!(mail_read(&mut buffer), BUF_LEN as isize);
|
||||
assert_eq!(buffer, [i as u8; BUF_LEN]);
|
||||
}
|
||||
println!("child read 16 mails succeed");
|
||||
assert_eq!(mail_read(&mut buffer), -1);
|
||||
println!("child read 1 mail fail");
|
||||
println!("child sleep 1s");
|
||||
sleep(1000 as usize);
|
||||
assert_eq!(mail_read(&mut buffer), BUF_LEN as isize);
|
||||
assert_eq!(buffer, [16 as u8; BUF_LEN]);
|
||||
println!("child read 1 mail succeed");
|
||||
println!("child exit");
|
||||
exit(0);
|
||||
}
|
||||
println!("I am father");
|
||||
println!("father sleep 1s");
|
||||
sleep(1000 as usize);
|
||||
for i in 0..16 {
|
||||
let buffer = [i as u8; BUF_LEN];
|
||||
assert_eq!(mail_write(pid as usize, &buffer), BUF_LEN as isize);
|
||||
}
|
||||
println!("father wirte 16 mails succeed");
|
||||
let buffer = [16 as u8; BUF_LEN];
|
||||
assert_eq!(mail_write(pid as usize, &buffer), -1);
|
||||
println!("father wirte 1 mail fail");
|
||||
println!("father sleep 1.5s");
|
||||
sleep(1500 as usize);
|
||||
assert_eq!(mail_write(pid as usize, &buffer), BUF_LEN as isize);
|
||||
println!("father wirte 1 mail succeed");
|
||||
|
||||
let mut xstate: i32 = -100;
|
||||
assert!(wait(&mut xstate) > 0);
|
||||
assert_eq!(xstate, 0);
|
||||
println!("mail2 test OK!");
|
||||
0
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate core;
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use core::slice;
|
||||
use user_lib::{getpid, mail_read, mail_write};
|
||||
|
||||
const BUF_LEN: usize = 256;
|
||||
const MAIL_MAX: usize = 16;
|
||||
const BAD_ADDRESS: usize = 0x90000000;
|
||||
|
||||
/// 邮箱错误参数测试,输出 mail3 test OK! 就算正确。
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let pid = getpid();
|
||||
let null = unsafe { slice::from_raw_parts(BAD_ADDRESS as *const _, 10) };
|
||||
assert_eq!(mail_write(pid as usize, &null), -1);
|
||||
let mut empty = ['a' as u8; 0];
|
||||
assert_eq!(mail_write(pid as usize, &empty), 0);
|
||||
assert_eq!(mail_read(&mut empty), -1);
|
||||
let buffer0 = ['a' as u8; BUF_LEN];
|
||||
for _ in 0..MAIL_MAX {
|
||||
assert_eq!(mail_write(pid as usize, &buffer0), BUF_LEN as isize);
|
||||
}
|
||||
assert_eq!(mail_write(pid as usize, &empty), -1);
|
||||
assert_eq!(mail_read(&mut empty), 0);
|
||||
assert_eq!(mail_write(pid as usize, &empty), -1);
|
||||
println!("mail3 test OK!");
|
||||
0
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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("test_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("test_exit0\0"), spawn("test_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
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
static TESTS: &[&str] = &[
|
||||
"test_sleep\0",
|
||||
"test_sleep1\0",
|
||||
"test_mmap0\0",
|
||||
"test_mmap1\0",
|
||||
"test_mmap2\0",
|
||||
"test_mmap3\0",
|
||||
"test_unmap\0",
|
||||
"test_unmap2\0",
|
||||
"test_spawn0\0",
|
||||
"test_spawn1\0",
|
||||
"test_mail0\0",
|
||||
"test_mail1\0",
|
||||
"test_mail2\0",
|
||||
"test_mail3\0",
|
||||
"test_file0\0",
|
||||
"test_file1\0",
|
||||
"test_file2\0",
|
||||
];
|
||||
|
||||
use user_lib::{exec, fork, waitpid};
|
||||
|
||||
/// 辅助测例,运行所有其他测例。
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
for test in TESTS {
|
||||
println!("Usertests: Running {}", test);
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
exec(*test, &[0 as *const u8]);
|
||||
panic!("unreachable!");
|
||||
} else {
|
||||
let mut exit_code: i32 = Default::default();
|
||||
let wait_pid = waitpid(pid as usize, &mut exit_code);
|
||||
assert_eq!(pid, wait_pid);
|
||||
println!(
|
||||
"\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m",
|
||||
test, pid, exit_code
|
||||
);
|
||||
}
|
||||
}
|
||||
println!("ch7 Usertests passed!");
|
||||
0
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
use user_lib::{write, STDOUT};
|
||||
const DATA_STRING: &'static str = "string from data section\n";
|
||||
|
||||
/// 正确输出:
|
||||
/// string from data section
|
||||
/// strinstring from stack section
|
||||
/// strin
|
||||
/// Test write1 OK!
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
assert_eq!(write(1234, DATA_STRING.as_bytes()), -1);
|
||||
assert_eq!(
|
||||
write(STDOUT, DATA_STRING.as_bytes()),
|
||||
DATA_STRING.len() as isize
|
||||
);
|
||||
assert_eq!(write(STDOUT, &DATA_STRING.as_bytes()[..5]), 5);
|
||||
let stack_string = "string from stack section\n";
|
||||
assert_eq!(
|
||||
write(STDOUT, stack_string.as_bytes()),
|
||||
stack_string.len() as isize
|
||||
);
|
||||
assert_eq!(write(STDOUT, &stack_string.as_bytes()[..5]), 5);
|
||||
println!("\nTest write1 OK!");
|
||||
0
|
||||
}
|
Loading…
Reference in new issue