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>