diff --git a/README.md b/README.md
index d4ec105..02af090 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ Other arguments are the same as ArceOS's [Makefile](https://github.com/arceos-or
 For example, to run the [httpserver](rust/net/httpserver/) on `qemu-system-aarch64` with 4 cores and log level `info`:
 
 ```bash
-make A=apps/net/httpserver ARCH=aarch64 LOG=info SMP=4 run NET=y
+make A=rust/net/httpserver ARCH=aarch64 LOG=info SMP=4 run NET=y
 ```
 
 Note that the `NET=y` argument is required to enable the network device in QEMU. These arguments (`BLK`, `GRAPHIC`, etc.) only take effect at runtime not build time.
@@ -76,7 +76,7 @@ Note that the `NET=y` argument is required to enable the network device in QEMU.
 | [helloworld](rust/helloworld/) | | | A minimal app that just prints a string |
 | [exception](rust/exception/) | | | Exception handling test |
 | [memtest](rust/memtest/) | alloc | axalloc | Dynamic memory allocation test |
-| [display](rust/display/) | display | axdisplay | Graphic/GUI test |
+| [display](rust/display/) | display | axdriver, axdisplay | Graphic/GUI test |
 | [yield](rust/task/yield/) | multitask | axalloc, axtask | Multi-threaded yielding test |
 | [sleep](rust/task/sleep/) | multitask, irq | axalloc, axtask | Thread sleeping test |
 | [parallel](rust/task/parallel/) | alloc, multitask | axalloc, axtask | Parallel computing test (to test synchronization & mutex) |
@@ -85,7 +85,7 @@ Note that the `NET=y` argument is required to enable the network device in QEMU.
 | [shell](rust/fs/shell/) | alloc, fs | axalloc, axdriver, axfs | A simple shell that responds to filesystem operations |
 | [httpclient](rust/net/httpclient/) | net | axalloc, axdriver, axnet | A simple client that sends an HTTP request and then prints the response |
 | [udpserver](rust/net/udpserver/) | net | axalloc, axdriver, axnet | A single-threaded echo server using UDP protocol |
-| [echoserver](rust/net/echoserver/) | alloc, multitask, net | axalloc, axdriver, axnet, axtask | A multi-threaded TCP server that reverses messages sent by the client  |
+| [echoserver](rust/net/echoserver/) | alloc, multitask, net | axalloc, axdriver, axnet, axtask | A multi-threaded TCP server that reverses messages sent by the client |
 | [httpserver](rust/net/httpserver/) | alloc, multitask, net | axalloc, axdriver, axnet, axtask | A multi-threaded HTTP server that serves a static web page |
 | [bwbench](rust/net/bwbench/) | net | axalloc, axdriver, axnet | Network bandwidth benchmark |
 
diff --git a/c/httpserver/httpserver.c b/c/httpserver/httpserver.c
index b6f7ac5..5ea7a79 100644
--- a/c/httpserver/httpserver.c
+++ b/c/httpserver/httpserver.c
@@ -24,7 +24,7 @@ const char content[] = "<html>\n\
   </center>\n\
   <hr>\n\
   <center>\n\
-    <i>Powered by <a href=\"https://github.com/arceos-org/arceos/tree/main/apps/net/httpserver\">ArceOS example HTTP server</a> v0.1.0</i>\n\
+    <i>Powered by <a href=\"https://github.com/arceos-org/arceos-apps/tree/main/c/httpserver\">ArceOS example HTTP server</a> v0.1.0</i>\n\
   </center>\n\
 </body>\n\
 </html>\n\
diff --git a/c/iperf/README.md b/c/iperf/README.md
index ca6f146..8a0d5a2 100644
--- a/c/iperf/README.md
+++ b/c/iperf/README.md
@@ -6,7 +6,7 @@ Build and start the [`iperf3`](https://github.com/esnet/iperf) server on ArceOS:
 
 ```bash
 # in arceos root directory
-make A=apps/c/iperf BLK=y NET=y ARCH=<arch> run
+make A=c/iperf BLK=y NET=y ARCH=<arch> run
 ```
 
 ## Benchmark
diff --git a/c/redis/README.md b/c/redis/README.md
index f5c2441..da18a45 100644
--- a/c/redis/README.md
+++ b/c/redis/README.md
@@ -1,8 +1,8 @@
 
 # How to run?
 - Run:
-  - `make A=apps/c/redis/ LOG=error NET=y BLK=y ARCH=aarch64 SMP=4 run`(for aarch64)
-  - `make A=apps/c/redis/ LOG=error NET=y BLK=y ARCH=x86_64 SMP=4 run`(for x86_64)
+  - `make A=c/redis/ LOG=error NET=y BLK=y ARCH=aarch64 SMP=4 run`(for aarch64)
+  - `make A=c/redis/ LOG=error NET=y BLK=y ARCH=x86_64 SMP=4 run`(for x86_64)
 
 # How to test?
 - Use `redis-cli -p 5555` to connect to redis-server, and enjoy ArceOS-Redis world!
@@ -269,7 +269,7 @@ MSET (10 keys): 183150.19 requests per second
 
 ## Compile and Run
 
-- `make A=apps/c/redis LOG=error PLATFORM=x86_64-pc-oslab SMP=4 FEATURES=driver-ixgbe,driver-ramdisk IP=10.2.2.2 GW=10.2.2.1`
+- `make A=c/redis LOG=error PLATFORM=x86_64-pc-oslab SMP=4 FEATURES=driver-ixgbe,driver-ramdisk IP=10.2.2.2 GW=10.2.2.1`
 - Copy `redis_x86_64-pc-oslab.elf` to `/boot`, then reboot.
 - Enter `grub` then boot the PC by ArceOS Redis.
 - Connect to ArceOS-Redis server by:
diff --git a/docs/apps_display.md b/docs/apps_display.md
new file mode 100644
index 0000000..bf16634
--- /dev/null
+++ b/docs/apps_display.md
@@ -0,0 +1,106 @@
+# INTRODUCTION
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [display](../rust/display/) | display | axdriver, axdisplay | Graphic/GUI test |
+
+# RUN
+
+```bash
+make A=rust/display GRAPHIC=y LOG=debug run
+```
+
+# RESULT
+
+```text
+...
+[  0.408067 axdriver:59] Initialize device drivers...
+[  0.410941 driver_virtio:50] Detected virtio MMIO device with vendor id: 0x554D4551, device type: GPU, version: Legacy
+[  0.414473 virtio_drivers::device::gpu:47] Device features EDID | NOTIFY_ON_EMPTY | ANY_LAYOUT | RING_INDIRECT_DESC | RING_EVENT_IDX
+[  0.418886 virtio_drivers::device::gpu:57] events_read: 0x0, num_scanouts: 0x1
+[  0.423408 virtio_drivers::device::gpu:102] => RespDisplayInfo { header: CtrlHeader { hdr_type: Command(4353), flags: 0, fence_id: 0, ctx_id: 0, _padding: 0 }, rect: Rect { x: 0, y: 0, width: 1280, height: 800 }, enabled: 1, flags: 0 }
+[  0.452037 axdriver::virtio:88] created a new Display device: "virtio-gpu"
+[  0.455473 axdisplay:17] Initialize Display subsystem...
+[  0.458124 axdisplay:19] number of Displays: 1
+...
+...
+...
+(never end)
+```
+![display](figures/display.png)
+
+# STEPS
+
+## step1
+
+``` rust
+let mut board = DrawingBoard::new();
+board.disp.clear(Rgb888::BLACK).unwrap();
+```
+
+**flow chart**
+```mermaid
+graph TD;
+    A["DrawingBoard::new()"] --> B["display::Display::new()"];
+    A --> C["embedded_graphics::prelude::Point::new(INIT_X, INIT_Y)"];
+    B --> D["libax::display::framebuffer_info"]
+    B --> E["core::slice::from_raw_parts_mut"]
+    B --> F["embedded_graphics::prelude::Size::new"]
+    D --> G["axsync::Mutex(axdriver::DisplayDevices)::lock"]
+    D --> H["axdriver::DisplayDevices::info"]
+```
+
+## step2
+``` rust
+for _ in 0..5 {
+        board.latest_pos.x += RECT_SIZE as i32 + 20;
+        board.paint();
+        framebuffer_flush();
+    }
+...
+impl DrawingBoard {
+    ...
+    fn paint(&mut self) {
+        Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
+            .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 10))
+            .draw(&mut self.disp)
+            .ok();
+        Circle::new(self.latest_pos + Point::new(-70, -300), 150)
+            .into_styled(PrimitiveStyle::with_fill(Rgb888::BLUE))
+            .draw(&mut self.disp)
+            .ok();
+        Triangle::new(
+            self.latest_pos + Point::new(0, 150),
+            self.latest_pos + Point::new(80, 200),
+            self.latest_pos + Point::new(-120, 300),
+        )
+        .into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 10))
+        .draw(&mut self.disp)
+        .ok();
+        let text = "ArceOS";
+        Text::with_alignment(
+            text,
+            self.latest_pos + Point::new(0, 300),
+            MonoTextStyle::new(&FONT_10X20, Rgb888::YELLOW),
+            Alignment::Center,
+        )
+        .draw(&mut self.disp)
+        .ok();
+    }
+}
+```
+
+**flow chart**
+```mermaid
+graph TD;
+    A["DrawingBoard::paint"] --> B["embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle, Triangle}"];
+    A --> C["embedded_graphics::text::{Alignment, Text}"]
+    B --> D["impl embedded_graphics::draw_target::DrawTarget, embedded_graphics::prelude::OriginDimensions for Display"]
+    C --> D
+```
+
+## step3
+``` rust
+loop {
+    core::hint::spin_loop();
+}
+```
diff --git a/docs/apps_echoserver.md b/docs/apps_echoserver.md
new file mode 100644
index 0000000..1c62cdf
--- /dev/null
+++ b/docs/apps_echoserver.md
@@ -0,0 +1,106 @@
+# INTRODUCTION
+
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [echoserver](../rust/net/echoserver/) | alloc, multitask, net | axalloc, axdriver, axnet, axtask | A multi-threaded TCP server that reverses messages sent by the client |
+
+# RUN
+
+```console
+$ make A=rust/net/echoserver NET=y run
+...
+Hello, echo server!
+listen on: 10.0.2.15:5555
+```
+
+In another shell, use `telnet` (or `nc`) to connect to localhost (`127.0.0.1`) to view the reversed echo message:
+
+```console
+$ telnet localhost 5555
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+hello
+olleh
+12345
+54321
+```
+
+```console
+$ nc 127.0.0.1 5555
+hello
+olleh
+12345
+54321
+```
+
+# STEPS
+
+## step1
+
+[init](./init.md)
+
+After executed all initial actions, then arceos calls `main` function in `echoserver` app.
+
+## step2
+
+`main` calls `accept_loop()`, which will keep processing incoming tcp connection.
+
+```rust
+let (addr, port) = (IpAddr::from_str(LOCAL_IP).unwrap(), LOCAL_PORT);
+let mut listener = TcpListener::bind((addr, port).into())?;
+println!("listen on: {}", listener.local_addr().unwrap());
+
+let mut i = 0;
+loop {
+    match listener.accept() {
+        ...
+        }
+        Err(e) => return Err(e),
+    }
+    i += 1;
+}
+```
+
+## step3
+
+Once it receives a tcp connection. It will get a `(stream, addr)` pair from `libax::net`.
+`main` task will spawn a task to reverse every package it receives.
+
+```rust
+Ok((stream, addr)) => {
+    info!("new client {}: {}", i, addr);
+    task::spawn(move || match echo_server(stream) {
+        Err(e) => error!("client connection error: {:?}", e),
+        Ok(()) => info!("client {} closed successfully", i),
+    });
+}
+```
+
+## step4
+
+Reverse bytes in package it receives.
+
+```rust
+fn reverse(buf: &[u8]) -> Vec<u8> {
+    let mut lines = buf
+        .split(|&b| b == b'\n')
+        .map(Vec::from)
+        .collect::<Vec<_>>();
+    for line in lines.iter_mut() {
+        line.reverse();
+    }
+    lines.join(&b'\n')
+}
+
+fn echo_server(mut stream: TcpStream) -> io::Result {
+    let mut buf = [0u8; 1024];
+    loop {
+        let n = stream.read(&mut buf)?;
+        if n == 0 {
+            return Ok(());
+        }
+        stream.write_all(reverse(&buf[..n]).as_slice())?;
+    }
+}
+```
diff --git a/docs/apps_exception.md b/docs/apps_exception.md
new file mode 100644
index 0000000..9a49542
--- /dev/null
+++ b/docs/apps_exception.md
@@ -0,0 +1,66 @@
+# INTRODUCTION
+
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [exception](../rust/exception/) | | | Exception handling test |
+
+# RUN
+
+```console
+$ make A=rust/exception LOG=debug run
+...
+Running exception tests...
+[  0.249873 0 axhal::arch::riscv::trap:13] Exception(Breakpoint) @ 0xffffffc0802001e8
+Exception tests run OK!
+[  0.068358 0 axtask::api:6] main task exited: exit_code=0
+[  0.069128 0 axhal::platform::qemu_virt_riscv::misc:2] Shutting down...
+```
+
+# STEPS
+
+## step1
+
+[init](./init.md)
+
+After executed all initial actions, then arceos calls `main` function in `exception` app.
+
+## step2
+
+``` Rust
+fn raise_break_exception() {
+    unsafe {
+        #[cfg(target_arch = "x86_64")]
+        asm!("int3");
+        #[cfg(target_arch = "aarch64")]
+        asm!("brk #0");
+        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
+        asm!("ebreak");
+    }
+}
+
+#[no_mangle]
+fn main() {
+    println!("Running exception tests...");
+    raise_break_exception();
+    println!("Exception tests run OK!");
+}
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+    A[" asm!(ebreak)"] --> B["raise exception"];
+    B --> C["axhal::arch::riscv::trap.S::trap_vector_base"];
+    C --> D["switch sscratch and sp"];
+    C -- "from U mode" --> E["Ltrap_entry_u: SAVE_REGS 1; a1 <-- 1"];
+    C -- "from S mode" --> F["Ltrap_entry_s: SAVE_REGS 0; a1 <-- 0"];
+    E --> G[axhal::arch::riscv::trap::riscv_trap_handler];
+    F --> G;
+    G -- "Trap::Exception(E::Breakpoint)" --> H["handle_breakpoint(&mut tf.sepc)"];
+    H --> I["debug!(Exception(Breakpoint) @ {:#x} , sepc);*sepc += 2;"];
+    I -- "from U mode" --> J["Ltrap_entry_u: RESTORE_REGS 1"];
+    I -- "from S mode" --> K["Ltrap_entry_s: RESTORE_REGS 0"];
+    J --> L[sret];
+    K --> L;
+```
diff --git a/docs/apps_fs_shell.md b/docs/apps_fs_shell.md
new file mode 100644
index 0000000..3d8c3ca
--- /dev/null
+++ b/docs/apps_fs_shell.md
@@ -0,0 +1,310 @@
+# INTRODUCTION
+
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [shell](../rust/fs/shell/) | alloc, fs | axalloc, axdriver, axfs | A simple shell that responds to filesystem operations |
+
+# RUN
+
+Before running the app, make an image of FAT32:
+
+```shell
+make disk_img
+```
+
+Run the app:
+
+```shell
+make A=rust/fs/shell ARCH=aarch64 LOG=debug BLK=y run
+```
+
+# RESULT
+
+```
+...
+[  0.006204 0 axdriver:64] Initialize device drivers...
+[  0.006396 0 driver_virtio:50] Detected virtio MMIO device with vendor id: 0x554D4551, device type: Block, version: Legacy
+[  0.006614 0 virtio_drivers::device::blk:55] device features: SEG_MAX | GEOMETRY | BLK_SIZE | SCSI | FLUSH | TOPOLOGY | CONFIG_WCE | DISCARD | WRITE_ZEROES | NOTIFY_ON_EMPTY | RING_INDIRECT_DESC | RING_EVENT_IDX
+[  0.007094 0 virtio_drivers::device::blk:64] config: 0xffff00000a003f00
+[  0.007270 0 virtio_drivers::device::blk:69] found a block device of size 34000KB
+[  0.007956 0 axdriver::virtio:88] created a new Block device: "virtio-blk"
+[  0.008488 0 axfs:25] Initialize filesystems...
+[  0.008584 0 axfs:26]   use block device: "virtio-blk"
+[  0.025432 0 axalloc:57] expand heap memory: [0xffff00004012f000, 0xffff00004013f000)
+[  0.025680 0 axalloc:57] expand heap memory: [0xffff00004013f000, 0xffff00004015f000)
+[  0.026510 0 axfs::fs::fatfs:122] create Dir at fatfs: /dev
+[  0.043112 0 axfs::fs::fatfs:102] lookup at fatfs: /dev
+[  0.049562 0 fatfs::dir:140] Is a directory
+[  0.057550 0 axruntime:137] Initialize interrupt handlers...
+[  0.057870 0 axruntime:143] Primary CPU 0 init OK.
+Available commands:
+  cat
+  cd
+  echo
+  exit
+  help
+  ls
+  mkdir
+  pwd
+  rm
+  uname
+arceos:/$
+```
+
+# STEPS
+
+## Step1
+
+[init](./init.md)
+
+After executed all initial actions, then arceos calls `main` function in `shell` app.
+
+## Step2
+
+The program reads one command from `stdin` each time and pass it to `cmd::run_cmd`.
+
+```rust
+fn main() {
+    let mut stdin = libax::io::stdin();
+    let mut stdout = libax::io::stdout();
+
+    let mut buf = [0; MAX_CMD_LEN];
+    let mut cursor = 0;
+    cmd::run_cmd("help".as_bytes());
+    print_prompt();
+
+    loop {
+        if stdin.read(&mut buf[cursor..cursor + 1]).ok() != Some(1) {
+            continue;
+        }
+        if buf[cursor] == b'\x1b' {
+            buf[cursor] = b'^';
+        }
+        match buf[cursor] {
+            CR | LF => {
+                println!();
+                if cursor > 0 {
+                    cmd::run_cmd(&buf[..cursor]);
+                    cursor = 0;
+                }
+                print_prompt();
+            }
+            BS | DL => {
+                if cursor > 0 {
+                    stdout.write(&[BS, SPACE, BS]).unwrap();
+                    cursor -= 1;
+                }
+            }
+            0..=31 => {}
+            c => {
+                if cursor < MAX_CMD_LEN - 1 {
+                    stdout.write(&[c]).unwrap();
+                    cursor += 1;
+                }
+            }
+        }
+    }
+}
+```
+
+## Step3
+
+The commands are parsed and executed in `cmd::run_cmd`.
+
+```rust
+pub fn run_cmd(line: &[u8]) {
+    let line_str = unsafe { core::str::from_utf8_unchecked(line) };
+    let (cmd, args) = split_whitespace(line_str);
+    if !cmd.is_empty() {
+        for (name, func) in CMD_TABLE {
+            if cmd == *name {
+                func(args);
+                return;
+            }
+        }
+        println!("{}: command not found", cmd);
+    }
+}
+```
+
+**flowchart**
+
+```mermaid
+graph TD
+	run_cmd["cmd::run_cmd"]
+
+	cat["cmd::do_cat"]
+	cd["cmd:do_cd"]
+	echo["cmd::do_echo"]
+	exit["cmd::do_exit"]
+	help["cmd::do_help"]
+	ls["cmd::do_ls"]
+	mkdir["cmd::do_mkdir"]
+	pwd["cmd::do_pwd"]
+	rm["cmd::do_rm"]
+	uname["cmd::do_uname"]
+
+	run_cmd --> cat
+	run_cmd --> cd
+	run_cmd --> echo
+	run_cmd --> exit
+	run_cmd --> help
+	run_cmd --> ls
+	run_cmd --> mkdir
+	run_cmd --> pwd
+	run_cmd --> rm
+	run_cmd --> uname
+
+  stdout_w["libax::io::stdout().write()"]
+	fopen["libax::fs::File::open"]
+	fread["libax::fs::file::File::read"]
+	fcreate["libax::fs::File::create"]
+	fwrite["libax::fs::file::File::write"]
+	fs_meta[libax::fs::metadata]
+	fs_readdir[libax::fs::read_dir]
+	fs_createdir[libax::fs::create_dir]
+	fs_rmdir[libax::fs::remove_dir]
+	fs_rmfile[libax::fs::remove_file]
+
+	cat --> fopen
+	cat --> fread
+	cat --> stdout_w
+	cd --> set_dir["libax::env::set_current_dir"]
+	echo --> fcreate
+	echo --> fwrite
+	exit --> lib_exit["libax::task::exit"]
+	ls --> get_dir["libax::env::current_dir"]
+	ls --> fs_meta
+	ls --> fs_readdir
+	mkdir --> fs_createdir
+	pwd --> get_dir
+	rm --> fs_meta
+	rm --> fs_rmdir
+	rm --> fs_rmfile
+```
+
+For the details of the file system APIs included in the chart, see the section below.
+
+# File system APIs
+
+> **Notes for the flow charts below**: normal lines denote the calling stack, while dashed lines denote the returning of results.
+
+### Create files, open files, and get metadata
+
+```mermaid
+graph TD
+  lib_create[libax::fs::File::create] --> |WRITE/CREATE/TRUNCATE| open_opt
+  lib_open[libax::fs::File::open] --> |READ ONLY| open_opt
+  lib_meta[libax::fs::metadata] --> lib_open1[libax::fs::File::open] --> |READ ONLY| open_opt
+  lib_meta --> f_meta[axfs::fops::File::get_attr] --> vfs_getattr
+  lib_meta -..-> |not found/permission denied| err(Return error)
+	open_opt["axfs::api::file::OpenOptions::open"] --> fops_open
+
+	fops_open["axfs::fops::File::open"] --> fops_openat
+	fops_openat["axfs::fops::File::_open_at"]
+	fops_openat --> lookup
+	fops_openat --> |w/ CREATE flag| create_file
+	fops_openat --> vfs_getattr
+
+	lookup["axfs::root::lookup"] --> vfs_lookup
+	create_file["axfs::root::create_file"]
+	create_file --> vfs_lookup
+	create_file --> vfs_create
+	create_file --> vfs_truncate
+	create_file --> vfs_open
+
+	vfs_lookup["axfs_vfs::VfsNodeOps::lookup"] --> fs_impl
+	vfs_create["axfs_vfs::VfsNodeOps::create"] --> fs_impl
+	vfs_getattr["axfs_vfs::VfsNodeOps::get_attr"] --> fs_impl
+	vfs_truncate[axfs_vfs::VfsNodeOps::truncate] --> fs_impl
+	vfs_open[axfs_vfs::VfsNodeOps::open] --> fs_impl
+	fs_impl[[FS implementation]]
+```
+
+
+
+### Create directories
+
+```mermaid
+graph TD
+	lib_mkdir[libax::fs::create_dir] --> builder_create["axfs::api::DirBuilder::create"]
+	builder_create --> root_create[axfs::root::create_dir] --> lookup[axfs::root::lookup]
+	lookup -..-> |exists/other error| err(Return error)
+	root_create -->|type=VfsNodeType::Dir| node_create[axfs_vfs::VfsNodeOps::create] --> fs_impl[[FS implementation]]
+
+	lookup --> vfs_lookup["axfs_vfs::VfsNodeOps::lookup"] --> fs_impl[[FS implementation]]
+```
+
+
+
+### Read and write files
+
+```mermaid
+graph LR
+  lib_read[libax::fs::File::read] --> fops_read
+  fops_read[axfs::fops::File::read] ---> |w/ read permission| vfs_read_at
+  vfs_read_at[axfs_vfs::VfsNodeOps::read] --> fs_impl[[FS implementation]]
+
+	lib_write[libax::fs::File::write] --> fops_write
+  fops_write[axfs::fops::File::write] ---> |w/ write permission| vfs_write_at
+  vfs_write_at[axfs_vfs::VfsNodeOps::write] --> fs_impl[[FS implementation]]
+
+  fops_read -.-> |else| err1(Return error)
+  fops_write -.-> |else| err(Return error)
+```
+
+### Get current directory
+
+```mermaid
+graph LR
+	lib_gwd[libax::fs::current_dir] --> root_gwd[axfs::root::current_dir]
+	lib_gwd -.-> return("Return path")
+```
+
+### Set current directory
+
+```mermaid
+graph TD
+  lib_cd[libax::fs::set_current_dir] --> root_cd[axfs::root::set_current_dir]
+
+  root_cd -..-> |is root| change[Set CURRENT_DIR and CURRENT_DIR_PATH]
+  root_cd --> |else| lookup["axfs::root::lookup"]
+
+  vfs_lookup["axfs_vfs::VfsNodeOps::lookup"]
+  lookup --> vfs_lookup --> fs_impl[[FS implementation]]
+  lookup -..-> |found & is directory & has permission| change
+
+```
+
+### Remove directory
+
+```mermaid
+graph TD
+	lib_rmdir[libax::fs::remove_dir] --> root_rmdir[axfs::root::remove_dir]
+
+  root_rmdir -.-> |empty/is root/invalid/permission denied| ret_err(Return error)
+
+  root_rmdir --> lookup[axfs::root::lookup] --> vfs_lookup["axfs_vfs::VfsNodeOps::lookup"] ---> fs_impl[[FS implementation]]
+  lookup -...-> |not found| ret_err
+
+  root_rmdir --> meta[axfs_vfs::VfsNodeOps::get_attr] --> fs_impl
+  meta -..-> |not a dir/permission denied| ret_err
+
+  root_rmdir --> remove_[axfs_vfs::VfsNodeOps::remove] ---> fs_impl
+
+```
+
+### Remove file
+
+```mermaid
+graph TD
+	lib_rm[libax::fs::remove_file] --> root_rm[axfs::root::remove_file]
+
+  root_rm --> lookup[axfs::root::lookup] --> vfs_lookup["axfs_vfs::VfsNodeOps::lookup"]
+  	---> fs_impl[[FS implementation]]
+  lookup -.-> |not found| ret_err
+  root_rm ---> meta[axfs_vfs::VfsNodeOps::get_attr] ---> fs_impl
+  meta -..-> |not a file/permission denied| ret_err(Return error)
+  root_rm --> remove_[axfs_vfs::VfsNodeOps::remove] ---> fs_impl
+
+```
diff --git a/docs/apps_helloworld.md b/docs/apps_helloworld.md
new file mode 100644
index 0000000..a98d5f0
--- /dev/null
+++ b/docs/apps_helloworld.md
@@ -0,0 +1,59 @@
+# INTRODUCTION
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [helloworld](../rust/helloworld/) | | | A minimal app that just prints a string |
+
+# RUN
+
+```shell
+make A=rust/helloworld SMP=4 LOG=debug run
+```
+
+# STEPS
+
+## step1
+[init](./init.md)
+
+After executed all initial actions, then arceos calls `main` function in `helloworld` app.
+
+## step2
+
+```Rust
+fn main() {
+    libax::println!("Hello, world!");
+}
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+    A[main] --> B["libax::println!(Hello, world!)"];
+    B --> C[libax:io::__print_impl];
+    C --> D[INLINE_LOCK=Mutex::new];
+    C --> _guard=INLINE_LOCK.lock;
+    C --> E["stdout().write_fmt(args)"];
+```
+
+### step2.1
+
+```mermaid
+graph TD;
+    T["stdout()"] --> A["libax::io::stdio.rs::stdout()"];
+    A --> B["INSTANCE: Mutex<StdoutRaw> = Mutex::new(StdoutRaw)"];
+    A --> C["return Stdout { inner: &INSTANCE }"];
+```
+
+### step2.2
+
+```mermaid
+graph TD;
+    T["stdout().write_fmt(args)"] --> A["Stdout::write"];
+    A --> B["self.inner.lock().write(buf)"];
+    B --> C["StdoutRaw::write"];
+    C --> D["axhal::console::write_bytes(buf);"];
+    C --> E["Ok(buf.len())"];
+    D --> F["putchar"];
+    F --> G["axhal::platform::qemu_virt_riscv::console::putchar"];
+    G --> H["sbi_rt::legacy::console_putchar"];
+```
diff --git a/docs/apps_httpclient.md b/docs/apps_httpclient.md
new file mode 100644
index 0000000..67bba80
--- /dev/null
+++ b/docs/apps_httpclient.md
@@ -0,0 +1,88 @@
+# INTRODUCTION
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [httpclient](../rust/net/httpclient/) | net | axalloc, axdriver, axnet | A simple client that sends an HTTP request and then prints the response |
+
+# RUN
+```bash
+make A=rust/net/httpclient SMP=1 NET=y LOG=debug run
+```
+
+# RESULT
+```text
+...
+[  0.065494 0 axalloc:128] initialize global allocator at: [0xffffffc080286000, 0xffffffc088000000)
+[  0.068109 0 axruntime:115] Initialize kernel page table...
+[  0.070549 0 axdriver:59] Initialize device drivers...
+[  0.072131 0 driver_virtio:50] Detected virtio MMIO device with vendor id: 0x554D4551, device type: Network, version: Legacy
+[  0.074003 0 virtio_drivers::device::net:117] Device features CTRL_GUEST_OFFLOADS | MAC | GSO | MRG_RXBUF | STATUS | CTRL_VQ | CTRL_RX | CTRL_VLAN | CTRL_RX_EXTRA | GUEST_ANNOUNCE | CTL_MAC_ADDR | RING_INDIRECT_DESC | RING_EVENT_IDX
+[  0.077999 0 virtio_drivers::device::net:127] Got MAC=[52, 54, 00, 12, 34, 56], status=LINK_UP
+[  0.080748 0 axalloc:57] expand heap memory: [0xffffffc080298000, 0xffffffc0802a8000)
+[  0.082357 0 axalloc:57] expand heap memory: [0xffffffc0802a8000, 0xffffffc0802c8000)
+[  0.083769 0 axalloc:57] expand heap memory: [0xffffffc0802c8000, 0xffffffc080308000)
+[  0.085864 0 axdriver::virtio:88] created a new Net device: "virtio-net"
+[  0.087057 0 axnet:22] Initialize network subsystem...
+[  0.087859 0 axnet:24] number of NICs: 1
+[  0.088517 0 axnet:27]   NIC 0: "virtio-net"
+[  0.089360 0 axalloc:57] expand heap memory: [0xffffffc080308000, 0xffffffc080408000)
+[  0.092033 0 axnet::smoltcp_impl:273] created net interface "eth0":
+[  0.093315 0 axnet::smoltcp_impl:275]   ether:    52-54-00-12-34-56
+[  0.094667 0 axnet::smoltcp_impl:277]   ip:       10.0.2.15/24
+[  0.095947 0 axnet::smoltcp_impl:278]   gateway:  10.0.2.2
+[  0.097154 0 axruntime:134] Initialize interrupt handlers...
+[  0.098892 0 axruntime:140] Primary CPU 0 init OK.
+Hello, simple http client!
+[  0.100960 0 axnet::smoltcp_impl:67] socket #0: created
+[  0.103106 0 smoltcp::iface::interface:1599] address 10.0.2.2 not in neighbor cache, sending ARP request
+HTTP/1.1 200 OK
+Server: nginx
+Date: Sat, 08 Apr 2023 18:58:27 GMT
+Content-Type: text/plain
+Content-Length: 13
+Connection: keep-alive
+Access-Control-Allow-Origin: *
+Cache-Control: no-cache, no-store, must-revalidate
+
+183.172.74.58
+[  0.585681 0 axnet::smoltcp_impl::tcp:148] socket #0: shutting down
+[  0.587517 0 axnet::smoltcp_impl:95] socket #0: destroyed
+...
+```
+
+# STEPS
+
+## step1
+``` rust
+let (addr, port) = (IpAddr::from_str(DEST_IP).unwrap(), 80);
+let mut stream = TcpStream::connect((addr, port).into())?;
+```
+
+**flow chart**
+```mermaid
+graph TD;
+    A["libax::tcp::TcpStream::connect"] --> B["smoltcp::wire::IpEndpoint::From(addr, port)"]
+    A --> C["axnet::smoltcp_impl::TcpSocket::new"]
+    A --> D["axnet::smoltcp_impl::TcpSocket::connect(addr)"]
+    C --> E["axsync::Mutex(smoltcp::iface::SocketSet)::new"]
+```
+
+## step2
+``` rust
+stream.write(REQUEST.as_bytes())?;
+
+let mut buf = [0; 1024];
+let n = stream.read(&mut buf)?;
+let response = core::str::from_utf8(&buf[..n]).unwrap();
+println!("{}", response);
+```
+
+**flow chart**
+```mermaid
+graph TD;
+    A["impl Read, Write for libax::TcpStream"] --> B["libax::TcpStream::read"]
+    A["impl Read, Write for libax::TcpStream"] --> C["libax::TcpStream::write"]
+    B --> D["smoltcp_impl::TcpSocket::recv(buf)"]
+    C --> E["smoltcp_impl::TcpSocket::send(buf)"]
+    D --> F["libax::println"]
+    E --> F
+```
diff --git a/docs/apps_httpserver.md b/docs/apps_httpserver.md
new file mode 100644
index 0000000..76a92b4
--- /dev/null
+++ b/docs/apps_httpserver.md
@@ -0,0 +1,177 @@
+# INTRODUCTION
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [httpserver](../rust/net/httpserver) | alloc, multitask, net | axalloc, axdriver, axnet, axtask | A multi-threaded HTTP server that serves a static web page |
+
+# RUN
+
+```shell
+make A=rust/net/httpserver SMP=4 NET=y LOG=info run
+```
+
+# RESULT
+
+```
+...
+[  0.101078 axtask:75]   use FIFO scheduler.
+[  0.102733 axdriver:59] Initialize device drivers...
+[  0.104475 driver_virtio:50] Detected virtio MMIO device with vendor id: 0x554D4551, device type: Network, version: Legacy
+[  0.107095 virtio_drivers::device::net:117] Device features CTRL_GUEST_OFFLOADS | MAC | GSO | MRG_RXBUF | STATUS | CTRL_VQ | CTRL_RX | CTRL_VLAN | CTRL_RX_EXTRA | GUEST_ANNOUNCE | CTL_MAC_ADDR | RING_INDIRECT_DESC | RING_EVENT_IDX
+[  0.113234 virtio_drivers::device::net:127] Got MAC=[52, 54, 00, 12, 34, 56], status=LINK_UP
+[  0.116326 axdriver::virtio:88] created a new Net device: "virtio-net"
+[  0.118063 axnet:22] Initialize network subsystem...
+[  0.119247 axnet:24] number of NICs: 1
+[  0.120442 axnet:27]   NIC 0: "virtio-net"
+[  0.121819 axalloc:57] expand heap memory: [0xffffffc080654000, 0xffffffc080a54000)
+[  0.124142 axalloc:57] expand heap memory: [0xffffffc080a54000, 0xffffffc081254000)
+[  0.127314 axnet::smoltcp_impl:273] created net interface "eth0":
+[  0.128951 axnet::smoltcp_impl:275]   ether:    52-54-00-12-34-56
+[  0.130706 axnet::smoltcp_impl:277]   ip:       10.0.2.15/24
+[  0.132189 axnet::smoltcp_impl:278]   gateway:  10.0.2.2
+[  0.133746 axruntime:134] Initialize interrupt handlers...
+...
+Hello, ArceOS HTTP server!
+...
+[  0.148419 0:2 axnet::smoltcp_impl:67] socket #0: created
+[  0.148850 0:2 axnet::smoltcp_impl::tcp:111] socket listening on 10.0.2.15:5555
+[  0.149305 0:2 axnet::smoltcp_impl:95] socket #0: destroyed
+listen on: http://10.0.2.15:5555/
+```
+
+Open http://127.0.0.1:5555/ in your browser, or use command lines in another shell to view the web page:
+
+```console
+$ curl http://127.0.0.1:5555/
+<html>
+<head>
+  <title>Hello, ArceOS</title>
+</head>
+<body>
+  <center>
+    <h1>Hello, <a href="https://github.com/rcore-os/arceos">ArceOS</a></h1>
+  </center>
+  <hr>
+  <center>
+    <i>Powered by <a href="https://github.com/arceos-org/arceos-apps/tree/main/rust/net/httpserver">ArceOS example HTTP server</a> v0.1.0</i>
+  </center>
+</body>
+</html>
+```
+
+# STEPS
+
+## step 1
+[init](./init.md)
+
+After executed all initial actions, then arceos calls `main` function in `memtest` app.
+
+## step 2
+```Rust
+fn http_server(mut stream: TcpStream) -> io::Result {
+    ...
+    stream.read(&mut buf)?;
+	...
+    stream.write_all(reponse.as_bytes())?;
+    ...
+}
+
+fn accept_loop() -> io::Result {
+    let (addr, port) = (IpAddr::from_str(LOCAL_IP).unwrap(), LOCAL_PORT);
+    let mut listener = TcpListener::bind((addr, port).into())?;
+    ...
+    loop {
+        match listener.accept() {
+            Ok((stream, addr)) => {
+                task::spawn(move || match http_server(stream) {
+                    Err(e) => error!("client connection error: {:?}", e),
+                    Ok(()) => info!("client {} closed successfully", i),
+                });
+            }
+            Err(e) => return Err(e),
+        }
+        i += 1;
+    }
+}
+
+#[no_mangle]
+fn main() {
+    println!("Hello, ArceOS HTTP server!");
+    accept_loop().expect("test HTTP server failed");
+}
+```
+
+### step 2.1
+
+```Rust
+let (addr, port) = (IpAddr::from_str(LOCAL_IP).unwrap(), LOCAL_PORT);
+let mut listener = TcpListener::bind((addr, port).into())?;
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+T["libax::net::tcp::TcpStream::bind()"]
+T-->A["smoltcp::wire::ip::IpAddr::from_str()"]
+T-->B["axnet::smoltcp_impl::tcp::TcpSocket::new()"]
+T-->C["axnet::smoltcp_impl::tcp::TcpSocket::bind()"]
+T-->D["axnet::smoltcp_impl::tcp::TcpSocket::listen()"]
+B-->E["axnet::smoltcp_impl::SocketSetWrapper::new_tcp_socket()"]
+E-->F["axsync::mutex< smoltcp::iface::socket_set::SocketSet >"]
+F-->G["managed::slice::ManagedSlice< smoltcp::iface::socket_set::SocketStorage >"]
+G-->H["smoltcp::iface::socket_meta::Meta + smoltcp::socket::Socket"]
+H-->I["SocketHandle + NeighborState"]
+H-->J["Raw + Icmp + udp + tcp"]
+
+D-->K["ListenTable: Box< [Mutex< Option< Box< ListenTableEntry > > >] >(tcp)"]
+K-->L["ListenTableEntry: VecDeque< SocketHandle >(syn_queue)"]
+L-->M["Tcp is a multi-wrapped sync queue of SocketHandle"]
+```
+
+### step 2.2
+
+```Rust
+match listener.accept() {
+    Ok((stream, addr)) => {
+		...
+    }
+    Err(e) => return Err(e),
+}
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+T["libax::net::tcp::TcpListener::accept()"]
+T-->A["axnet::smoltcp_impl::tcp::TcpSocket::accept()"]
+A-->B["Mutex< SocketSet >.poll_interfaces()"]
+B-->C["axnet::smoltcp_impl::InterfaceWrapper< axdriver::VirtIoNetDev >.poll"]
+C-->Z["many things"]
+A-->D["axnet::smoltcp_impl::listen_table::ListenTable::accept()"]
+D-->E["check the sync queue"]
+
+```
+
+
+
+
+
+### step 2.3
+
+```Rust
+stream.read(&mut buf)?;
+stream.write_all(reponse.as_bytes())?;
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+    A["impl Read, Write for libax::TcpStream"] --> B["libax::TcpStream::read"]
+    A["impl Read, Write for libax::TcpStream"] --> C["libax::TcpStream::write"]
+    B --> D["smoltcp_impl::TcpSocket::recv(buf)"]
+    C --> E["smoltcp_impl::TcpSocket::send(buf)"]
+
+```
+
diff --git a/docs/apps_memtest.md b/docs/apps_memtest.md
new file mode 100644
index 0000000..9340861
--- /dev/null
+++ b/docs/apps_memtest.md
@@ -0,0 +1,54 @@
+# INTRODUCTION
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [memtest](../rust/memtest/) | alloc | axalloc | Dynamic memory allocation test |
+# RUN
+
+```console
+$ make A=rust/memtest SMP=4 LOG=info run
+...
+Running memory tests...
+test_vec() OK!
+test_btree_map() OK!
+Memory tests run OK!
+[  0.523323 3 axhal::platform::qemu_virt_riscv::misc:2] Shutting down...
+...
+```
+
+# STEPS
+
+## step1
+
+[init](./init.md)
+
+After executed all initial actions, then arceos calls `main` function in `memtest` app.
+
+## step2
+```Rust
+fn test_vec() {
+...
+    let mut v = Vec::with_capacity(N);
+...
+}
+fn test_btree_map() {
+...
+    let mut m = BTreeMap::new();
+...
+}
+fn main() {
+    println!("Running memory tests...");
+    test_vec();
+    test_btree_map();
+    println!("Memory tests run OK!");
+}
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+A["rust.alloc runtime"] --> B["impl GlobalAlloc for  axalloc::GlobalAllocator alloc()"];
+B --> C["axalloc::GlobalAllocator::alloc()"];
+C --> D["simple two-level allocator: if no heap memory, allocate from the page allocator"];
+
+```
diff --git a/docs/apps_parallel.md b/docs/apps_parallel.md
new file mode 100644
index 0000000..699acfe
--- /dev/null
+++ b/docs/apps_parallel.md
@@ -0,0 +1,184 @@
+# INTRODUCTION
+
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [parallel](../rust/task/parallel/) | alloc, multitask | axalloc, axtask | Parallel computing test (to test synchronization & mutex) |
+
+# RUN
+
+## Without preemption (FIFO scheduler)
+
+```shell
+make A=rust/task/parallel LOG=info run
+```
+
+## With preemption (RR scheduler)
+
+```shell
+make A=rust/task/parallel LOG=info APP_FEATURES=preempt run
+```
+
+## Using multicore
+
+```shell
+make A=rust/task/parallel LOG=info SMP=4 run
+```
+
+# RESULT
+
+```console
+$ make A=rust/task/parallel APP_FEATURES=preempt SMP=4 run
+...
+part 0: TaskId(7) [0, 125000)
+part 3: TaskId(10) [375000, 500000)
+part 1: TaskId(8) [125000, 250000)
+part 2: TaskId(9) [250000, 375000)
+part 4: TaskId(11) [500000, 625000)
+part 5: TaskId(12) [625000, 750000)
+part 6: TaskId(13) [750000, 875000)
+part 7: TaskId(14) [875000, 1000000)
+part 8: TaskId(15) [1000000, 1125000)
+part 9: TaskId(16) [1125000, 1250000)
+part 10: TaskId(17) [1250000, 1375000)
+part 11: TaskId(18) [1375000, 1500000)
+part 12: TaskId(19) [1500000, 1625000)
+part 13: TaskId(20) [1625000, 1750000)
+part 14: TaskId(21) [1750000, 1875000)
+part 15: TaskId(22) [1875000, 2000000)
+part 15: TaskId(22) finished
+part 3: TaskId(10) finished
+part 2: TaskId(9) finished
+part 1: TaskId(8) finished
+part 0: TaskId(7) finished
+part 7: TaskId(14) finished
+part 4: TaskId(11) finished
+part 6: TaskId(13) finished
+part 5: TaskId(12) finished
+part 8: TaskId(15) finished
+part 10: TaskId(17) finished
+part 9: TaskId(16) finished
+part 11: TaskId(18) finished
+part 13: TaskId(20) finished
+part 14: TaskId(21) finished
+part 12: TaskId(19) finished
+main task woken up! timeout=false
+sum = 61783189038
+Parallel summation tests run OK!
+[  1.219708 3:2 axhal::platform::qemu_virt_aarch64::psci:25] Shutting down...
+```
+
+# PROCESS
+
+`main`使用`MAIN_WQ`睡眠 500ms,并检查`main`的唤醒是因为时间到(而非其他`task`的`notify()`)。
+
+`main`调用`task::spawn`产生`NUM_TASKS`个`task`,分别进行计算。计算完毕后,使用一个`WaitQueue`(`static BARRIER_WQ`)以等待其他`task`的完成。
+在全部`task`完成后,执行`BARRIER_WQ.notify_all(true)`,继续各`task`的执行。
+
+`main`在生成`task`后,调用`MAIN_WQ.wait_timeout()`等待 600ms,随后检查`task`的计算结果。
+
+# FUNCTIONS
+
+## barrier
+
+`BARRIER_COUNT += 1`,记录已经完成计算的`task`数量。
+
+`BARRIER_WQ.wait_until()`,block 至所有`task`均完成计算。
+
+`BARRIER_WQ.notify_all()`,唤醒`BARRIER_WQ`内的所有 task 继续执行。
+
+# STEPS
+
+## step1
+
+[init](./init.md)
+
+After executed all initial actions, then arceos calls `main` function in `parallel` app.
+
+## step2
+
+Calculate expected value from tasks.
+
+```rust
+let vec = Arc::new(
+    (0..NUM_DATA)
+        .map(|_| rand::rand_u32() as u64)
+        .collect::<Vec<_>>(),
+);
+let expect: u64 = vec.iter().map(sqrt).sum();
+```
+
+## step3
+
+Sleep `main` task in `MAIN_WQ` for 500ms. `main` **must** be timed out to wake up since there's no other task to `notify()` it.
+
+```rust
+let timeout = MAIN_WQ.wait_timeout(Duration::from_millis(500));
+assert!(timeout);
+```
+
+## step4
+
+`main` task spawn all `NUM_TASKS` tasks.
+
+```rust
+for i in 0..NUM_TASKS {
+    let vec = vec.clone();
+    task::spawn(move || {
+        ...
+    });
+}
+```
+
+Each task will do the calculation, then call `barrier()`.
+
+```rust
+// task:
+let left = i * (NUM_DATA / NUM_TASKS);
+let right = (left + (NUM_DATA / NUM_TASKS)).min(NUM_DATA);
+println!(
+    "part {}: {:?} [{}, {})",
+    i,
+    task::current().id(),
+    left,
+    right
+);
+
+RESULTS.lock()[i] = vec[left..right].iter().map(sqrt).sum();
+
+barrier();
+
+println!("part {}: {:?} finished", i, task::current().id());
+let n = FINISHED_TASKS.fetch_add(1, Ordering::Relaxed);
+if n == NUM_TASKS - 1 {
+    MAIN_WQ.notify_one(true);
+}
+
+fn barrier() {
+    static BARRIER_WQ: WaitQueue = WaitQueue::new();
+    static BARRIER_COUNT: AtomicUsize = AtomicUsize::new(0);
+    BARRIER_COUNT.fetch_add(1, Ordering::Relaxed);
+    BARRIER_WQ.wait_until(|| BARRIER_COUNT.load(Ordering::Relaxed) == NUM_TASKS);
+    BARRIER_WQ.notify_all(true);
+}
+```
+
+`barrier()` will keep track of how many tasks have finished calculation in `BARRIER_COUNT`.
+
+Task will sleep in `BARRIER_WQ` until all tasks have finished. Then, the first awake task will `notify_all()` tasks to wake up.
+
+Task will print some info, add 1 to `FINISHED_TASKS`. The last task (`n == NUM_TASKS - 1`) will notify the `main` task to wake up.
+
+## step5
+
+`main` will sleep 600ms in `MAIN_WQ` after spawning all the tasks. Once awake, `main` will check the actual calculation results.
+
+```rust
+let timeout = MAIN_WQ.wait_timeout(Duration::from_millis(600));
+println!("main task woken up! timeout={}", timeout);
+
+let actual = RESULTS.lock().iter().sum();
+println!("sum = {}", actual);
+assert_eq!(expect, actual);
+
+println!("Parallel summation tests run OK!");
+```
diff --git a/docs/apps_priority.md b/docs/apps_priority.md
new file mode 100644
index 0000000..9fc2bc9
--- /dev/null
+++ b/docs/apps_priority.md
@@ -0,0 +1,42 @@
+# INTRODUCTION
+
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [priority](../rust/task/priority/) | alloc, multitask, sched_fifo, sched_rr, sched_cfs | axalloc, axtask | Task priority test |
+
+# RUN
+```shell
+make A=rust/task/priority ARCH=riscv64 SMP=1 APP_FEATURES=sched_cfs run LOG=info
+```
+Other choises of APP_FEATURES: sched_fifo, sched_rr
+
+## Using multicore
+```shell
+make A=rust/task/sched-realtime ARCH=riscv64 SMP=4 APP_FEATURES=sched_cfs run LOG=info
+```
+Other choises of APP_FEATURES: sched_fifo, sched_rr
+
+# RESULT
+```console
+$ make A=rust/task/priority ARCH=riscv64 SMP=1 APP_FEATURES=sched_cfs run LOG=info
+...
+part 0: TaskId(4) [0, 40)
+part 1: TaskId(5) [0, 40)
+part 2: TaskId(6) [0, 40)
+part 3: TaskId(7) [0, 40)
+part 4: TaskId(8) [0, 4)
+part 3: TaskId(7) finished
+part 4: TaskId(8) finished
+part 2: TaskId(6) finished
+part 1: TaskId(5) finished
+part 0: TaskId(4) finished
+sum = 3318102132
+leave time:
+task 0 = 614ms
+task 1 = 479ms
+task 2 = 374ms
+task 3 = 166ms
+task 4 = 371ms
+Priority tests run OK!
+[  1.274073 0:2 axhal::platform::qemu_virt_riscv::misc:3] Shutting down...
+```
diff --git a/docs/apps_sleep.md b/docs/apps_sleep.md
new file mode 100644
index 0000000..7e442d2
--- /dev/null
+++ b/docs/apps_sleep.md
@@ -0,0 +1,122 @@
+# INTRODUCTION
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [sleep](../rust/task/sleep) | multitask, irq | axalloc, axtask | Thread sleeping test |
+
+# RUN
+
+```
+make A=rust/task/sleep SMP=4 LOG=debug run
+```
+
+# RESULT
+```
+...
+
+[  0.077898 axruntime:109] Initialize global memory allocator...
+[  0.080584 axalloc:128] initialize global allocator at: [0xffffffc08033c000, 0xffffffc088000000)
+[  0.084562 axruntime:115] Initialize kernel page table...
+[  0.088103 axtask:69] Initialize scheduling...
+[  0.090130 axtask::task:113] new task: Task(1, "idle")
+[  0.091833 axalloc:57] expand heap memory: [0xffffffc08034a000, 0xffffffc08035a000)
+
+...
+
+[  9.205714 1:2 axtask::run_queue:127] task sleep: Task(2, "main"), deadline=9.2151238s
+[  9.207086 1:6 axtask:144] idle task: waiting for IRQs...
+[  9.207282 2:1 axtask::run_queue:49] task yield: Task(1, "idle")
+[  9.208362 2:1 axtask:144] idle task: waiting for IRQs...
+[  9.209530 0:4 axtask::run_queue:114] task unblock: Task(11, "")
+[  9.210129 0:4 axtask::run_queue:49] task yield: Task(4, "idle")
+[  9.210775 0:11 arceos_sleep:40] task 3 actual sleep 4.0016563s seconds (1).
+task 3 sleep 4 seconds (2) ...
+
+...
+
+Sleep tests run OK!
+[ 16.228092 2:2 axtask::run_queue:80] task exit: Task(2, "main"), exit_code=0
+[ 16.228823 2:2 axhal::platform::qemu_virt_riscv::misc:2] Shutting down...
+```
+
+# STEPS
+
+## step1
+[init](./init.md)
+After executed all initial actions, then arceos calls `main` function in `helloworld` app.
+
+## step2
+```Rust
+fn main(){
+...
+}
+```
+
+### step2.1
+```Rust
+    println!("Hello, main task!");
+    let now = Instant::now();
+    task::sleep(Duration::from_secs(1));
+    let elapsed = now.elapsed();
+    println!("main task sleep for {:?}", elapsed);
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+    S["libax::task::sleep()"]
+
+    S-->arg["libax::time::Duration::from_secs()"]
+    arg-->argA["libax::time::Duration::new(secs)"]
+
+	S-->A["axhal::time::wall_time()"]
+	A-->AA["axhal::time::TimeValue::from_nanos()"]
+
+	S-->B["axtask::run_queue::AxRunQueue::sleep_until()"]
+	B-->B.lock["SpinNoIrq< axtask::run_queue::AxRunQueue >"]
+	B-->BA["axtask::timers::set_alarm_wakeup()"]
+	BA-->BAA["SpinNoIrq < timer_list::TimeList< axtask::timers::TaskWakeupEvent>>"]
+	BAA-->BAAA["BinaryHeap < timer_list::TimerEventWrapper >"]
+	BAA-->BAAB["AxTaskRef = Arc< AxTask >"]
+	BAAB-->BAABA["scheduler::FifoTask< axtask::task::TaskInner >"]
+	B-->BB["axtask::run_queue::resched_inner()"]
+	BB-->BBA["axtask::task::set_state()"]
+```
+
+### step2.2
+
+```Rust
+	task::spawn(|| {
+        for i in 0..30 {
+            info!("  tick {}", i);
+            task::sleep(Duration::from_millis(500));
+        }
+    });
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+    T["axtask::task::spawn(closure fn)"]
+    T-->A["axtask::task::TaskInner::new(entry, name, axconfig::TASK_STACK_SIZE)"]
+    A-->B["axtask::task::TaskInner::new_common(axtask::task::TaskId, name)"]
+    A-->C["axtask::task::TaskStack::alloc(size)"]
+```
+
+### step2.3
+
+```Rust
+	while FINISHED_TASKS.load(Ordering::Relaxed) < NUM_TASKS {
+        task::sleep(Duration::from_millis(10));
+    }
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+    T["FINISHED_TASKS.load(Ordering::Relaxed)"]
+    T --> A["core::sync::atomic::AtomicUsize::new(num)"]
+    T --> B["core::sync::atomic::Ordering::Relaxed"]
+```
diff --git a/docs/apps_yield.md b/docs/apps_yield.md
new file mode 100644
index 0000000..0e86efb
--- /dev/null
+++ b/docs/apps_yield.md
@@ -0,0 +1,91 @@
+# INTRODUCTION
+| App | Enabled features | Extra modules | Description |
+|-|-|-|-|
+| [yield](../rust/task/yield/) | multitask | axalloc, axtask | Multi-threaded yielding test |
+
+# RUN
+
+## Without preemption (FIFO scheduler)
+
+```shell
+make A=rust/task/yield ARCH=riscv64 LOG=info NET=y SMP=1 run
+```
+
+## With preemption (RR scheduler)
+
+```shell
+make A=rust/task/yield ARCH=riscv64 LOG=info NET=y SMP=1 APP_FEATURES=preempt run
+```
+
+## RESULT
+
+```
+Hello, main task!
+Hello, task 0! id = TaskId(4)
+Hello, task 1! id = TaskId(5)
+Hello, task 2! id = TaskId(6)
+Hello, task 3! id = TaskId(7)
+Hello, task 4! id = TaskId(8)
+Hello, task 5! id = TaskId(9)
+Hello, task 6! id = TaskId(10)
+Hello, task 7! id = TaskId(11)
+Hello, task 8! id = TaskId(12)
+Hello, task 9! id = TaskId(13)
+Task yielding tests run OK!
+```
+
+# STEPS
+
+## step1
+
+* OS init
+* After executed all initial actions, then arceos call main function in `yield` app.
+
+## step2
+
+* Use the `task::spawn` cycle to generate `NUM_TASKS` tasks (similar to threads).
+* Each task executes a function, just print its ID.
+* If preemption is disabled, the task voluntarily executes `yield` to give up the CPU.
+* If SMP is not enabled, the execution order of tasks must be FIFO.
+* `main task` will wait for all other tasks to complete. If not, continue to execute `yield` and wait.
+
+```rust
+fn main() {
+    for i in 0..NUM_TASKS {
+        task::spawn(move || {
+            println!("Hello, task {}! id = {:?}", i, task::current().id());
+            // 此时已经启动了yield
+            // 因为preempt所需要的依赖libax/sched_rr并没有被包含进去
+            #[cfg(not(feature = "preempt"))]
+            task::yield_now();
+
+            let order = FINISHED_TASKS.fetch_add(1, Ordering::Relaxed);
+            if option_env!("SMP") == Some("1") {
+                assert!(order == i); // FIFO scheduler
+            }
+        });
+    }
+    println!("Hello, main task{}!");
+    while FINISHED_TASKS.load(Ordering::Relaxed) < NUM_TASKS {
+        #[cfg(not(feature = "preempt"))]
+        task::yield_now();
+    }
+    println!("Task yielding tests run OK!");
+}
+```
+
+**flow chart**
+
+```mermaid
+graph TD;
+    T["main task"] --> A["axtask::lib::spawn"];
+    A -- "task_i" --> B["axtask::run_queue::AxRunqQueue.scheduler.push_back(tasak_i)"];
+    A --> C["RUN_QUEUE.lock()"];
+    B -- "repeat n times" --> A;
+    B -- "main task" --> D["axtask::lib::yield_now"];
+    D --> E["axtask::run_queue::AxRunqQueue::resched_inner"];
+    E -- "prev" --> F["axtask::run_queue::AxRunqQueue.scheduler.push_back(prev)"];
+    E -- "next=axtask::run_queue::AxRunqQueue.scheduler.pop_front()" --> G["axtask::run_queue::AxRunqQueue:: switch_to(prev,next)"];
+    G -- "repeat n times" --> D;
+    G -- "all n subtask finished" --> H["finished"]
+```
diff --git a/docs/figures/display.png b/docs/figures/display.png
new file mode 100644
index 0000000..3bfab41
Binary files /dev/null and b/docs/figures/display.png differ
diff --git a/docs/init.md b/docs/init.md
new file mode 100644
index 0000000..3218fe7
--- /dev/null
+++ b/docs/init.md
@@ -0,0 +1,44 @@
+```mermaid
+graph TD;
+    A[axhal::platform::qemu_virt_riscv::boot.rs::_boot] --> init_boot_page_table;
+    A --> init_mmu;
+    A --> P[platform_init];
+    A --> B[axruntime::rust_main];
+    P --> P1["axhal::mem::clear_bss()"];
+    P --> P2["axhal::arch::riscv::set_trap_vector_base()"];
+    P --> P3["axhal::cpu::init_percpu()"];
+    P --> P4["axhal::platform::qemu_virt_riscv::irq.rs::init()"];
+    P --> P5["axhal::platform::qemu_virt_riscv::time.rs::init()"];
+    B --> axlog::init;
+    B --> D[init_allocator];
+    B --> remap_kernel_memory;
+    B --> axtask::init_scheduler;
+    B --> axdriver::init_drivers;
+    B --> Q[axfs::init_filesystems];
+    B --> axnet::init_network;
+    B --> axdisplay::init_display;
+    B --> init_interrupt;
+    B --> mp::start_secondary_cpus;
+    B --> C[main];
+    Q --> Q1["disk=axfs::dev::Disk::new()"];
+    Q --> Q2["axfs::root::init_rootfs(disk)"];
+    Q2 --fatfs--> Q21["main_fs=axfs::fs::fatfs::FatFileSystem::new()"];
+    Q2 --> Q22["MAIN_FS.init_by(main_fs); MAIN_FS.init()"];
+    Q2 --> Q23["root_dir = RootDirectory::new(MAIN_FS)"];
+    Q2 --devfs--> Q24["axfs_devfs::DeviceFileSystem::new()"];
+    Q2 --devfs--> Q25["devfs.add(null, zero, bar)"];
+    Q2 -->Q26["root_dir.mount(devfs)"];
+    Q2 -->Q27["init ROOT_DIR, CURRENT_DIR"];
+    D --> E["In free memory_regions: axalloc::global_init"];
+    D --> F["In free memory_regions:  axalloc::global_add_memory"];
+    E --> G[axalloc::GLOBAL_ALLOCATOR.init];
+    F --> H[axalloc::GLOBAL_ALLOCATOR.add_memory];
+    G --> I["PAGE: self.palloc.lock().init"];
+    G --> J["BYTE: self.balloc.lock().init"];
+    H --> K["BYTE: self.balloc.lock().add_memory"];
+    I --> M["allocator::bitmap::BitmapPageAllocator::init()"];
+    J -->L["allocator::slab::SlabByteAllocator::init() self.inner = unsafe { Some(Heap::new(start, size))"];
+    K --> N["allocator::slab::SlabByteAllocator::add_memory:  self.inner_mut().add_memory(start, size);"];
+
+```
+
diff --git a/rust/net/httpserver/src/main.rs b/rust/net/httpserver/src/main.rs
index 6b18d12..dd06e38 100644
--- a/rust/net/httpserver/src/main.rs
+++ b/rust/net/httpserver/src/main.rs
@@ -42,7 +42,7 @@ const CONTENT: &str = r#"<html>
   </center>
   <hr>
   <center>
-    <i>Powered by <a href="https://github.com/arceos-org/arceos/tree/main/apps/net/httpserver">ArceOS example HTTP server</a> v0.1.0</i>
+    <i>Powered by <a href="https://github.com/arceos-org/arceos-apps/tree/main/rust/net/httpserver">ArceOS example HTTP server</a> v0.1.0</i>
   </center>
 </body>
 </html>