diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil.md" similarity index 100% rename from "source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil.md" rename to "source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil.md" diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\200\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\200\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..03187304144 --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\200\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,383 @@ +# 解决`rust-analyzer`的问题 + +创建:`.vscode/settings.json`: + +```json +{ +    "rust-analyzer.cargo.features": ["axstd"] +} +``` + +> 这里一定要注意对`rust-analyzer`进行`Restart Server`,这个操作也可以见[我的这篇博客](https://www.cnblogs.com/chenhan-winddevil/p/18402660). + +# 直接用vscode打开wsl环境下的工程 + +直接在`wsl`的`cmd`下输入`code .`. + +这里可能会显示在安装`VSCode Server`,不是在安装`VSCode`本身,而是在安装一个服务,安装好自己就帮你打开了. + +# 组件化内核 + +不同的内核要求就有不同的设计. + +Linux是宏内核就照着宏内核设计. + +那么组件化内核就照着组件化内核设计. + +复杂的内核=很多小的内核合起来的 + +宏内核和Hypervisor与Unikernel没有明显的界限,因为这实际上是一个设计模式. + +- 协作性 +- 定位容易,维护难度低 +- 开发效率高 + +用组件化内核替代原本的宏内核.但是这个内核是组件化开发的,最终组成一个总的内核. + +# UniKernel + +有一个非常不同的地方**应用和内核都在内核态并且在同一个地址空间里,互相是可见的**. + +> **RTOS**似乎就是一个**UniKernel**. + +那么打包完是同一个image. + +那么宏内核是内核是一个image应用是另一个image,用syscall来打通. + +因为是**同一特权级**,所以安全性不太行. + +那么如果UniKernel需要保证安全性,那么就需要Hypervisor来帮你解决这个问题. + +不同的组件可以用axruntime来组织. + +裸机->逻辑程序 + +裸机->unikernel->OS程序 + +用OS去实现RTOS. + +> 用裸机+虚拟化实现类似于`docker`的功能. + +# 实验支撑的开发 + +每一节课是一个针对每个需求的内核. + +那么新的内核是从旧的内核组建而成的. + +宏内核和Unikernel的区别是加了用户特权级和用户的地址空间.看起来是增加了两个组件形成的,实际上到了很远的一条路. + +让Unikernel实现Hypervisor,这样就可以实现虚拟化.从Host升级到Guest. + + 宏内核的东西比较复杂 + +# 实验环境的建立 + +使用WSL2+Ubuntu 22.04.2 LTS环境. + +[安装 WSL | Microsoft Learn](https://learn.microsoft.com/zh-cn/windows/wsl/install) + +[Windows Terminal - Windows官方下载 | 微软应用商店 | Microsoft Store](https://apps.microsoft.com/detail/9n0dx20hk701?rtc=1&hl=zh-cn&gl=CN) + + +# 框架 + +![](00%20inbox/asset/Pasted%20image%2020241113192552.png) + +# 引导过程 + +是通过axhal. + +实际上使用的是`_start`这个指针. + +通过一系列的asm操作来进行完成页表和函数调用和MMU的启动的支持. + +# 日志级别控制与features + +使用`Cargo.toml`来控制`features`. + +使用环境变量: +1. 具体环境变量 +2. 使用通用环境变量 + +三个部分汇集起来到axfeat + +# 课后练习-支持带颜色的打印输出 + +\[print_with_color\]: 支持带颜色的打印输出。 + +要求: +1. 修改一个组件的实现 +2. 执行make run A=exercises/print_with_color + +> 这一点非常重要 + +预期:字符串输出带颜色。(具体颜色不做要求) +提示:在不同层次的组件上修改,影响的输出范围不同。 +例如,修改axstd可能只影响println!的输出;修改axhal则可能一并影响ArceOS启动信息的颜色。 + +## 通过修改APP层实现 + +修改`exercises\print_with_color\src\main.rs`: + +```rust +... ... +fn main() { + println!("\x1b[31m[WithColor]: Hello, Arceos!\x1b[0m"); +} +``` + +> 分支名称:`print_with_color_app` + +## 通过修改`ulib:axstd`来实现 + +在`ulib\axstd\src\macros.rs`: +```rust +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => { + $crate::io::__print_impl(format_args!("\x1b[31m{}\x1b[0m", format_args!($($arg)*))); + } +} + +/// Prints to the standard output, with a newline. +#[macro_export] +macro_rules! println { + () => { $crate::print!("\n") }; + ($($arg:tt)*) => { + $crate::io::__print_impl(format_args!("\x1b[31m{}\n\x1b[0m", format_args!($($arg)*))); + } +} +``` + +> 分支名称:`print_with_color_axstd` +## 通过修改`axhal:write_bytes`来实现 + +修改`modules\axhal\src\lib.rs`: +```rust +... ... +pub mod console { + pub use super::platform::console::*; + + /// Write a slice of bytes to the console. + pub fn write_bytes(bytes: &[u8]) { + let color_begin = "\x1b[31m"; + let color_end = "\x1b[0m"; + for c in color_begin.bytes() { + putchar(c); + } + for c in bytes { + putchar(*c); + } + for c in color_end.bytes() { + putchar(c); + } + } +} +... ... +``` + +> 分支名称:`print_with_color_axhal` + +## 通过修改`axlog:ax_println`来实现(不了) + +可以看到:`modules\axruntime\src\lib.rs`里调用了这个宏, +```rust +... ... +pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { + ax_println!("{}", LOGO); + ax_println!( + "\ + arch = {}\n\ + platform = {}\n\ + target = {}\n\ + smp = {}\n\ + build_mode = {}\n\ + log_level = {}\n\ + ", + option_env!("AX_ARCH").unwrap_or(""), + option_env!("AX_PLATFORM").unwrap_or(""), + option_env!("AX_TARGET").unwrap_or(""), + option_env!("AX_SMP").unwrap_or(""), + option_env!("AX_MODE").unwrap_or(""), + option_env!("AX_LOG").unwrap_or(""), + ); + #[cfg(feature = "rtc")] + ax_println!( + "Boot at {}\n", + chrono::DateTime::from_timestamp_nanos(axhal::time::wall_time_nanos() as _), + ); +... ... +} +``` + +并且这个宏的位置在`modules\axlog\src\lib.rs`,我们修改它: +```rust +... ... +macro_rules! ax_println { + () => { $crate::ax_print!("\n") }; + ($($arg:tt)*) => { + $crate::__print_impl(format_args!("\x1b[31m{}\x1b[0m\n", format_args!($($arg)*))); + } +} +... ... +``` + +**这里只能使得如下部分变成红色**,而不能满足题意: +```shell + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = warn +``` + +> 分支名称:`print_with_color_axlog` + +# 问题和疑问 + +1. `make`是怎么编译的? +2. `axruntime`是怎么加载的编译好的APP并且运行的? +3. `sbi`用的什么? + +# 为`axstd`支持`Collections` + +具体可以看这里的介绍:[集合类型 - Rust语言圣经(Rust Course)](https://course.rs/basic/collections/intro.html) + +最开始我学`Rust`的时候没有内化这个概念. + +实际上集合类型就是`Vector`和`HashMap`以及`String`这样的类型. + +其实到这里我们自己就可以总结出了: + +类型长度可变->指针地址和大小变量存在栈上,具体内容存在堆上->需要堆的支持->需要动态内存分配器. + +那么实际上要支持`Collections`就是要支持一个动态内存分配器. + +`rCore`中对动态内存分配器的描述:[[rCore学习笔记 028] Rust 中的动态内存分配 - winddevil - 博客园](https://www.cnblogs.com/chenhan-winddevil/p/18442833) + +`rCore`中引用的动态内存分配器:[[rCore学习笔记 029] 动态内存分配器实现-以buddy_system_allocator源码为例 - winddevil - 博客园](https://www.cnblogs.com/chenhan-winddevil/p/18447537) + +![](00%20inbox/asset/Pasted%20image%2020241117150533.png) + +这里注意这张图,在`Unikernel`中,内存管理器也是和APP放在一起的,主要的思想还是**APP和Kernel在同一特权级**. + +## `alloc`实现接口 + +![](00%20inbox/asset/Pasted%20image%2020241117151558.png) + +这个是需要实现两个功能: +1. 实现`byteAllocator`和`pageAllocatir`. +2. `byteAllocator`只需要实现`rust`自带的一个`Trait`即`#[global_alloctor]`即可 +3. `pageAllocator`的实现方式是用了一个全局变量来实现相应的功能的 + +这里的源代码在`modules/axalloc/src/lib.rs`这里看. + +> 这里是实现了一个`GlobalAllocator`类,然后实例化一个 +> 1. 实现`GlobalAlloc`的`Trait`给它,有`alloc`和`dealloc`这两个方法,即可实现内存分配.用`#[global_alloctor]`标注这个实例即可. +> 2. 为`GlobalAllocator`实现了`alloc_pages`和`dealloc_pages`,通过直接获取这个实例 + +这里实现的时候`trait`的`fn`和`struct`本身的`fn`重合,这是允许的. + +> 调用时,`MyTrait::do_something(&struct)`和`struct.do_something`两种调用形式是调用的不同的方法. + + +实现方法: + +![](00%20inbox/asset/Pasted%20image%2020241117154427.png) + + +1. 最开始的时候页分配器是先分配一部分内存给字节分配器的 +2. 先找字节分配器分配,任何如果不够了找页分配器追加 + +在当前为了给`HashMap`提供分配器的是`core`库里`allocator`里自带的`TlsfByteAllocator`作为**字节分配器**,`BitmapPageAllocator`作为**页分配器**. + +那么为了实现自己的字节分配器就需要要给它实现两个`Trait`,即`BaseAllocator`和`ByteAllocator`. + +这个下一节可能会讲到. + +# 课后练习-支持HashMap类型 + +![](00%20inbox/asset/Pasted%20image%2020241114214732.png) + +这里要搞懂关于库的文件夹的互相依赖关系. + +## 为什么`core`里没`HashMap`? + +因为`HashMap`需要的随机数生成器涉及到**体系结构**. + +那么这里的`random()`是来自于`axhal`就非常的合理了. + +## 有关于`Cargo.toml` + +两个`Cargo.toml`: +1. `.\Cargo.toml` +2. `ulib\axstd\Cargo.toml` + +使用`cargo tree -p axstd -e features`可以很好地看到`features`结构. + +# 有关于结构问题 + +这里注意一点,在`ulib/axstd/src/lib.rs`里已经引用了`collections`, +```rust +pub use alloc::{boxed, collections, format, string, vec}; +``` + +而在`exercises/support_hashmap/src/main.rs`里引用的是: +```rust +use std::collections::HashMap; +``` + +在`rust`[这个项目](https://github.com/rust-lang/rust)里发现,`library\alloc`的`collection`里并没有`HashMap`,`HashMap`是在`library\std`的`collection`里. + +所以其实我们是要把这两个`collection`的内容合并. + +## 直接使用`hashbrown` + +创建`ulib/axstd/src/collections/mod.rs`: +```rust +// 合并alloc::collections里的内容 +pub use alloc::collections::*; +// 引用hashbrown +pub use hashbrown::HashMap; +``` + +在`ulib/axstd/Cargo.toml`加入`hashbrown`: +```toml +... ... +[dependencies] +... ... +hashbrown = "0.15.1" +... ... +``` + +## 完全仿写`std`库 + +自己实现以`hashbrown::HashMap`为`base`的`HashMap`. + +通过题中给出的`random`实现`RandomState`. + +想在`axstd`中调用`axhal/random`,需要在`ulib/axstd/Cargo.toml`里加入: +```toml +[dependencies] +axhal = { workspace = true } +``` + +`#TODO` +## 拉链法实现 + +`#TODO` + + + + + diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\203\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\203\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..14679cfca16 --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\203\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,118 @@ +# 概念 + +![](00%20inbox/asset/Pasted%20image%2020241204205737.png) + +关键是在于这个**虚拟化层**,我们用虚拟化层来实现对硬件抽象层的虚拟化. + +![](00%20inbox/asset/Pasted%20image%2020241204210024.png) + +虚拟化的效率高是因为`Hypervisor`的体系结构环境是相同的. + +![](00%20inbox/asset/Pasted%20image%2020241204210409.png) + +> 这里的II型是KVM这种.1.5型可能更多的是 + +![](00%20inbox/asset/Pasted%20image%2020241204210949.png) + +物理设备只有一个CPU的时候用多任务假装是`vcpu`. + +![](00%20inbox/asset/Pasted%20image%2020241204211120.png) + +> 有点像虚拟内存<->物理内存.用的也是页表的方式来建立对应关系. + +![](00%20inbox/asset/Pasted%20image%2020241204211208.png) + +> 和`vcpu`的方式是一样的实现`vDev`.其实就是怎么把一个设备的划分成很多小的粒度,然后再分配资源给它. + + +最简Hypervisor执行流程: +1. 加载Guest OS内核Image到新建地址空间。 +2. 准备虚拟机环境,设置特殊上下文。 +3. 结合特殊上下文和指令sret切换到V模式,即VM-ENTRY。 +4. OS内核只有一条指令,调用sbi-call的关机操作。 +5. 在虚拟机中,sbi-call超出V模式权限,导致VM-EXIT退出虚拟机,切换回Hypervisor。 +6. Hypervisor响应VM-EXIT的函数检查退出原因和参数,进行处理,由于是请求关机,清理虚拟机后,退出。 + + +> 这里这个`V`模式一定要注意,之前我们只涉及了`U`和`S`模式. + +![](00%20inbox/asset/Pasted%20image%2020241204212048.png) + +> `S`变成了`HS`. +> `HS`是关键,作为联通真实世界和虚拟世界的通道.体系结构设计了双向变迁机制. +> 后边实现的很多东西都来自于`HS`这个特权级的寄存器. + + +![](00%20inbox/asset/Pasted%20image%2020241204212340.png) + + +H扩展后,S模式发送明显变化:原有s\[xxx\]寄存器组作用不变,新增hs\[xxx\]和vs\[xxx\] +hs\[xxx\]寄存器组的作用:面向Guest进行路径控制,例如异常/中断委托等 +vs\[xxx\]寄存器组的作用:直接操纵Guest域中的VS,为其准备或设置状态 + +> 意思好像是要用`HS`里的`H`去假冒`VS`? + +![](00%20inbox/asset/Pasted%20image%2020241204212641.png) + +> 根据`spv`的不同决定`sret`会进入虚拟化还是进入`user`. + +![](00%20inbox/asset/Pasted%20image%2020241204212840.png) + +> 这里还多一个`spvp`,指示HS对V模式下地址空间是否有操作权限,1表示有权限操作,0无权限. + + +总结下来感觉虚拟机更像是一个进程,但是它又比进程多很多东西,需要运行自己的一套寄存器. + +# 实验 + +运行之后是触发了一个`IllegalInstruction`的`trap`. + +> 所以确实是会报错的. + +> 这里要注意是`QEMU`的版本问题,如果是默认的`6.x`会导致无法正常进入`vmexit_handler`. +> 我这里安装的`QEMU`是`9.1.0`版本,可以参见我自己的博客[[rCore学习笔记 03]配置rCore开发环境 - winddevil - 博客园](https://www.cnblogs.com/chenhan-winddevil/p/18292632),下载的时候把版本改成`9.1.0`即可. + +这里知道`li`指令访问了`a7`寄存器违反了不能访问`csr`的规定. + +由于`li`的长度为`4`,我们做出如下修改`tf.sepc += 4;`: +```rust +// modules/axhal/src/arch/riscv/trap.rs +#[no_mangle] +fn riscv_trap_handler(tf: &mut TrapFrame, from_user: bool) { + let scause: scause::Scause = scause::read(); + match scause.cause() { + #[cfg(feature = "uspace")] + Trap::Exception(E::UserEnvCall) => { + tf.regs.a0 = crate::trap::handle_syscall(tf, tf.regs.a7) as usize; + tf.sepc += 4; + } + Trap::Exception(E::LoadPageFault) => handle_page_fault(tf, MappingFlags::READ, from_user), + Trap::Exception(E::StorePageFault) => handle_page_fault(tf, MappingFlags::WRITE, from_user), + Trap::Exception(E::InstructionPageFault) => { + handle_page_fault(tf, MappingFlags::EXECUTE, from_user) + } + Trap::Exception(E::Breakpoint) => handle_breakpoint(&mut tf.sepc), + Trap::Interrupt(_) => { + handle_trap!(IRQ, scause.bits()); + } + Trap::Exception(E::IllegalInstruction) => { + tf.sepc += 4; + info!("Illegal Instruction"); + } + _ => { + panic!( + "Unhandled trap {:?} @ {:#x}:\n{:#x?}", + scause.cause(), + tf.sepc, + tf + ); + } + } +} +``` + +# 课后作业 + +> 注意触发缺页异常之后要调整`sepc`的值,防止再回去再触发缺页异常然后无限循环了. + +因为未知原因会卡住,这题的解题思路应该和`h_1_0`是一样的. diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\211\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\211\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..fb54ebedd6d --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\270\211\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,1295 @@ +# 抢占式调度 + +两个实验第二个比第一个多一些关于`wait_queue`的内容. + +CFS**公平调度**策略. + +抢占式调度的基本保障是**定时器**. + +ArceOS的抢占不是无条件的: +1. 内部条件:时间片耗尽 +2. 外部条件 + 1. 通过关抢占(锁)确定一段执行的过程中不会出现抢占,形成关抢占的临界区 + 2. 只有的特定的执行点上才会发生抢占(一个反向的临界区?) + +要内外部条件**都满足**. + +`preempt_disable_count`是多个结合的,因此计数会大于`1`.只有是`0`的情况下才可以被抢占. + +`CFS`比之前的那种抢占式调度不同,加了一种调度算法. + +`vruntime = init_vruntime + (delta / weight(nice))`. + +系统初始化时,`init_vruntime`, `delta`, `nice`三者都是`0`.但是我们可以人为根据偏好设置`init_vruntime`,但是随着系统运行时间的增加,`init_vruntime`的作用越来越小. + +`vruntime`最小的任务就是优先权最高任务,即当前任务. + +每次始终中断的时候都会递增`delta`(但是不会直接切换任务,而会运行**优先级最高**的任务),随着`delta`的递增,导致这个任务的优先级不够,然后就会被换掉. + +即使下次还是运行自己,还是会发生一次**无用切换**. + +## 实验验证 + +### 第一个实验 + +`make run A=tour/u_6_0` + +出现的log如下: +```shell +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x0000000087000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 + + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = warn + +Multi-task(Preemptible) is starting ... +worker1 ... ThreadId(4) +worker1 [0] +worker1 [1] +worker1 [2] +worker1 [3] +worker1 [4] +worker1 [5] +worker1 [6] +worker1 [7] +worker1 [8] +worker1 [9] +Wait for workers to exit ... +worker2 ... ThreadId(5) +worker2 [0] +worker2 [1] +worker2 [2] +worker2 [3] +worker2 [4] +worker2 [5] +worker2 [6] +worker2 [7] +worker2 [8] +worker2: nothing to do! +worker2: nothing to do! +worker2: nothing to do! +worker1 [10] +worker2 [9] +worker2: nothing to do! +worker2: nothing to do! +worker1 [11] +worker2 [10] +worker2: nothing to do! +worker1 [12] +worker1 [13] +worker2 [11] +worker2 [12] +worker1 [14] +worker1 [15] +worker2 [13] +worker2 [14] +worker2: nothing to do! +worker1 [16] +worker2 [15] +worker2: nothing to do! +worker1 [17] +worker1 [18] +worker2 [16] +worker2 [17] +worker1 [19] +worker1 [20] +worker2 [18] +worker2 [19] +worker2: nothing to do! +worker1 [21] +worker2 [20] +worker2: nothing to do! +worker1 [22] +worker1 [23] +worker2 [21] +worker2 [22] +worker1 [24] +worker1 [25] +worker2 [23] +worker2 [24] +worker1 [26] +worker1 [27] +worker1 [28] +worker2 [25] +worker2 [26] +worker1 [29] +worker1 [30] +worker2 [27] +worker2 [28] +worker1 [31] +worker1 [32] +worker1 [33] +worker2 [29] +worker2 [30] +worker1 [34] +worker1 [35] +worker2 [31] +worker2 [32] +worker1 [36] +worker1 [37] +worker1 [38] +worker2 [33] +worker2 [34] +worker1 [39] +worker1 [40] +worker2 [35] +worker2 [36] +worker2 [37] +worker1 [41] +worker1 [42] +worker2 [38] +worker2 [39] +worker1 [43] +worker1 [44] +worker2 [40] +worker2 [41] +worker2 [42] +worker1 [45] +worker1 [46] +worker2 [43] +worker2 [44] +worker1 [47] +worker1 [48] +worker2 [45] +worker2 [46] +worker2 [47] +worker1 [49] +worker1 [50] +worker2 [48] +worker2 [49] +worker1 [51] +worker1 [52] +worker2 [50] +worker2 [51] +worker2: nothing to do! +worker1 [53] +worker2 [52] +worker2: nothing to do! +worker1 [54] +worker1 [55] +worker2 [53] +worker2 [54] +worker1 [56] +worker1 [57] +worker2 [55] +worker2 [56] +worker1 [58] +worker1 [59] +worker1 [60] +worker2 [57] +worker2 [58] +worker1 [61] +worker1 [62] +worker2 [59] +worker2 [60] +worker2 [61] +worker1 [63] +worker1 [64] +worker2 [62] +worker2 [63] +worker1 [65] +worker1 [66] +worker2 [64] +worker2 [65] +worker2: nothing to do! +worker1 [67] +worker2 [66] +worker2: nothing to do! +worker1 [68] +worker1 [69] +worker2 [67] +worker2 [68] +worker1 [70] +worker1 [71] +worker2 [69] +worker2 [70] +worker2: nothing to do! +worker1 [72] +worker2 [71] +worker2: nothing to do! +worker1 [73] +worker2 [72] +worker2: nothing to do! +worker1 [74] +worker1 [75] +worker2 [73] +worker2 [74] +worker1 [76] +worker1 [77] +worker1 [78] +worker2 [75] +worker2 [76] +worker1 [79] +worker1 [80] +worker2 [77] +worker2 [78] +worker1 [81] +worker1 [82] +worker1 [83] +worker2 [79] +worker2 [80] +worker1 [84] +worker1 [85] +worker2 [81] +worker2 [82] +worker1 [86] +worker1 [87] +worker1 [88] +worker2 [83] +worker2 [84] +worker1 [89] +worker1 [90] +worker2 [85] +worker2 [86] +worker1 [91] +worker1 [92] +worker1 [93] +worker2 [87] +worker2 [88] +worker1 [94] +worker1 [95] +worker2 [89] +worker2 [90] +worker1 [96] +worker1 [97] +worker1 [98] +worker2 [91] +worker2 [92] +worker1 [99] +worker1 [100] +worker2 [93] +worker2 [94] +worker1 [101] +worker1 [102] +worker2 [95] +worker2 [96] +worker1 [103] +worker1 [104] +worker2 [97] +worker2 [98] +worker2 [99] +worker1 [105] +worker1 [106] +worker2 [100] +worker2 [101] +worker1 [107] +worker1 [108] +worker2 [102] +worker2 [103] +worker1 [109] +worker1 [110] +worker2 [104] +worker2 [105] +worker1 [111] +worker1 [112] +worker2 [106] +worker2 [107] +worker1 [113] +worker1 [114] +worker1 [115] +worker2 [108] +worker2 [109] +worker1 [116] +worker1 [117] +worker2 [110] +worker1 [118] +worker1 [119] +worker2 [111] +worker2 [112] +worker2 [113] +worker1 [120] +worker1 [121] +worker2 [114] +worker2 [115] +worker1 [122] +worker1 [123] +worker2 [116] +worker2 [117] +worker1 [124] +worker1 [125] +worker2 [118] +worker2 [119] +worker1 [126] +worker1 [127] +worker2 [120] +worker2 [121] +worker1 [128] +worker1 [129] +worker2 [122] +worker2 [123] +worker2 [124] +worker1 [130] +worker1 [131] +worker2 [125] +worker2 [126] +worker1 [132] +worker1 [133] +worker2 [127] +worker2 [128] +worker1 [134] +worker1 [135] +worker2 [129] +worker2 [130] +worker1 [136] +worker1 [137] +worker2 [131] +worker2 [132] +worker1 [138] +worker1 [139] +worker2 [133] +worker2 [134] +worker2 [135] +worker1 [140] +worker1 [141] +worker2 [136] +worker2 [137] +worker1 [142] +worker1 [143] +worker2 [138] +worker2 [139] +worker1 [144] +worker1 [145] +worker2 [140] +worker2 [141] +worker1 [146] +worker1 [147] +worker2 [142] +worker2 [143] +worker1 [148] +worker1 [149] +worker2 [144] +worker2 [145] +worker2 [146] +worker1 [150] +worker1 [151] +worker2 [147] +worker2 [148] +worker1 [152] +worker1 [153] +worker2 [149] +worker2 [150] +worker1 [154] +worker1 [155] +worker2 [151] +worker2 [152] +worker1 [156] +worker1 [157] +worker2 [153] +worker2 [154] +worker1 [158] +worker1 [159] +worker2 [155] +worker2 [156] +worker2 [157] +worker1 [160] +worker1 [161] +worker2 [158] +worker2 [159] +worker1 [162] +worker1 [163] +worker2 [160] +worker2 [161] +worker1 [164] +worker1 [165] +worker2 [162] +worker2 [163] +worker1 [166] +worker1 [167] +worker2 [164] +worker2 [165] +worker1 [168] +worker1 [169] +worker2 [166] +worker2 [167] +worker2 [168] +worker1 [170] +worker1 [171] +worker2 [169] +worker2 [170] +worker1 [172] +worker1 [173] +worker2 [171] +worker2 [172] +worker1 [174] +worker1 [175] +worker2 [173] +worker2 [174] +worker1 [176] +worker1 [177] +worker2 [175] +worker2 [176] +worker1 [178] +worker1 [179] +worker2 [177] +worker2 [178] +worker1 [180] +worker1 [181] +worker1 [182] +worker2 [179] +worker2 [180] +worker1 [183] +worker1 [184] +worker2 [181] +worker2 [182] +worker1 [185] +worker1 [186] +worker2 [183] +worker2 [184] +worker1 [187] +worker1 [188] +worker2 [185] +worker2 [186] +worker1 [189] +worker1 [190] +worker2 [187] +worker2 [188] +worker1 [191] +worker1 [192] +worker2 [189] +worker2 [190] +worker1 [193] +worker1 [194] +worker1 [195] +worker2 [191] +worker2 [192] +worker1 [196] +worker1 [197] +worker2 [193] +worker2 [194] +worker1 [198] +worker1 [199] +worker2 [195] +worker2 [196] +worker1 [200] +worker1 [201] +worker2 [197] +worker2 [198] +worker1 [202] +worker1 [203] +worker2 [199] +worker2 [200] +worker1 [204] +worker1 [205] +worker2 [201] +worker2 [202] +worker1 [206] +worker1 [207] +worker2 [203] +worker2 [204] +worker2 [205] +worker1 [208] +worker1 [209] +worker2 [206] +worker2 [207] +worker1 [210] +worker1 [211] +worker2 [208] +worker2 [209] +worker1 [212] +worker1 [213] +worker2 [210] +worker2 [211] +worker1 [214] +worker1 [215] +worker2 [212] +worker2 [213] +worker1 [216] +worker1 [217] +worker2 [214] +worker2 [215] +worker2 [216] +worker1 [218] +worker1 [219] +worker2 [217] +worker2 [218] +worker1 [220] +worker1 [221] +worker2 [219] +worker2 [220] +worker1 [222] +worker1 [223] +worker2 [221] +worker2 [222] +worker1 [224] +worker1 [225] +worker2 [223] +worker2 [224] +worker1 [226] +worker1 [227] +worker1 [228] +worker2 [225] +worker2 [226] +worker1 [229] +worker1 [230] +worker2 [227] +worker2 [228] +worker1 [231] +worker1 [232] +worker2 [229] +worker2 [230] +worker1 [233] +worker1 [234] +worker2 [231] +worker2 [232] +worker1 [235] +worker1 [236] +worker2 [233] +worker2 [234] +worker1 [237] +worker1 [238] +worker1 [239] +worker2 [235] +worker2 [236] +worker1 [240] +worker1 [241] +worker2 [237] +worker2 [238] +worker1 [242] +worker1 [243] +worker2 [239] +worker2 [240] +worker1 [244] +worker1 [245] +worker2 [241] +worker2 [242] +worker1 [246] +worker1 [247] +worker2 [243] +worker2 [244] +worker1 [248] +worker1 [249] +worker1 [250] +worker2 [245] +worker2 [246] +worker1 [251] +worker1 [252] +worker2 [247] +worker2 [248] +worker1 [253] +worker1 [254] +worker2 [249] +worker2 [250] +worker1 [255] +worker1 [256] +worker2 [251] +worker2 [252] +worker1 ok! +worker2 [253] +worker2 [254] +worker2 [255] +worker2 [256] +worker2 ok! +Multi-task(Preemptible) ok! +``` + +1. 最开始`worker1`运行了9次`println`,但是只运行了8次`push_back`,每次时间片耗尽触发了`cfs`调度算法,但是一直到这时候才轮到`worker2`的`vruntime`最小(**而不是触发时间片耗尽的`round_robin`**). +2. 随后`worker2`获取到**双端队列**之后,尝试输出队列最前边的内容.全部输出完之后,进入另一个分支输出`worker2: nothing to do!`,并且`yield`.但是由于`vruntime`仍然是`worker2`最小,因此又运行了两次,一共运行三次以后才切换回`worker1`.(**这说明`cfs`调度算法的`yield`是考虑切换,而不是直接把当前任务放进队列最后**) +3. `worker1`在输出`worker1 [10]`之后还没来得及把`10`压入队列,这时候又切换回`worker2`. +4. 这时候`worker2`发生了三次`yield`但是还是没有切换到`work1`.于是输出了一次`worker2 [9]`,又触发了两次`worker2: nothing to do!`. +5. 后边也都这样进行分析即可.可以看到在`cfs`算法中,即使你使用了`yield`还是可能切换回本任务. + +## 第二个实验 + +`make run A=tour/u_6_1` + +产生的log如下: +```shell +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x0000000087000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 + + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = warn + +WaitQ is starting ... +worker1 ... +worker1 [0] +worker1 [1] +worker1 [2] +worker1 [3] +worker1 [4] +worker2 ... +Wait for workers to exit ... +worker2 [0] +worker2 [1] +worker2 [2] +worker2 [3] +worker2 [4] +worker1 [5] +worker2 [5] +worker1 [6] +worker2 [6] +worker1 [7] +worker2 [7] +worker1 [8] +worker2 [8] +worker1 [9] +worker2 [9] +worker1 [10] +worker2 [10] +worker1 [11] +worker2 [11] +worker1 [12] +worker2 [12] +worker1 [13] +worker2 [13] +worker1 [14] +worker2 [14] +worker1 [15] +worker2 [15] +worker1 [16] +worker2 [16] +worker1 [17] +worker2 [17] +worker1 [18] +worker2 [18] +worker1 [19] +worker2 [19] +worker1 [20] +worker2 [20] +worker1 [21] +worker2 [21] +worker1 [22] +worker2 [22] +worker1 [23] +worker2 [23] +worker1 [24] +worker2 [24] +worker1 [25] +worker2 [25] +worker1 [26] +worker2 [26] +worker1 [27] +worker2 [27] +worker1 [28] +worker2 [28] +worker1 [29] +worker2 [29] +worker1 [30] +worker2 [30] +worker1 [31] +worker2 [31] +worker1 [32] +worker2 [32] +worker1 [33] +worker2 [33] +worker1 [34] +worker2 [34] +worker1 [35] +worker2 [35] +worker1 [36] +worker2 [36] +worker1 [37] +worker2 [37] +worker1 [38] +worker2 [38] +worker1 [39] +worker2 [39] +worker1 [40] +worker2 [40] +worker1 [41] +worker2 [41] +worker1 [42] +worker2 [42] +worker1 [43] +worker2 [43] +worker1 [44] +worker2 [44] +worker1 [45] +worker2 [45] +worker1 [46] +worker2 [46] +worker1 [47] +worker2 [47] +worker1 [48] +worker2 [48] +worker1 [49] +worker2 [49] +worker1 [50] +worker2 [50] +worker1 [51] +worker2 [51] +worker1 [52] +worker2 [52] +worker1 [53] +worker2 [53] +worker1 [54] +worker2 [54] +worker1 [55] +worker2 [55] +worker1 [56] +worker2 [56] +worker1 [57] +worker2 [57] +worker1 [58] +worker2 [58] +worker1 [59] +worker2 [59] +worker1 [60] +worker2 [60] +worker1 [61] +worker2 [61] +worker1 [62] +worker2 [62] +worker1 [63] +worker2 [63] +worker1 [64] +worker2 [64] +worker1 [65] +worker2 [65] +worker1 [66] +worker2 [66] +worker1 [67] +worker2 [67] +worker1 [68] +worker2 [68] +worker1 [69] +worker2 [69] +worker1 [70] +worker2 [70] +worker1 [71] +worker2 [71] +worker1 [72] +worker2 [72] +worker1 [73] +worker2 [73] +worker1 [74] +worker2 [74] +worker1 [75] +worker2 [75] +worker1 [76] +worker2 [76] +worker1 [77] +worker2 [77] +worker1 [78] +worker2 [78] +worker1 [79] +worker2 [79] +worker1 [80] +worker2 [80] +worker1 [81] +worker2 [81] +worker1 [82] +worker2 [82] +worker1 [83] +worker2 [83] +worker1 [84] +worker2 [84] +worker1 [85] +worker2 [85] +worker1 [86] +worker2 [86] +worker1 [87] +worker2 [87] +worker1 [88] +worker2 [88] +worker1 [89] +worker2 [89] +worker1 [90] +worker2 [90] +worker1 [91] +worker2 [91] +worker1 [92] +worker2 [92] +worker1 [93] +worker2 [93] +worker1 [94] +worker2 [94] +worker1 [95] +worker2 [95] +worker1 [96] +worker2 [96] +worker1 [97] +worker2 [97] +worker1 [98] +worker2 [98] +worker1 [99] +worker2 [99] +worker1 [100] +worker2 [100] +worker1 [101] +worker2 [101] +worker1 [102] +worker2 [102] +worker1 [103] +worker2 [103] +worker1 [104] +worker2 [104] +worker1 [105] +worker2 [105] +worker1 [106] +worker2 [106] +worker1 [107] +worker2 [107] +worker1 [108] +worker2 [108] +worker1 [109] +worker2 [109] +worker1 [110] +worker2 [110] +worker1 [111] +worker2 [111] +worker1 [112] +worker2 [112] +worker1 [113] +worker2 [113] +worker1 [114] +worker2 [114] +worker1 [115] +worker2 [115] +worker1 [116] +worker2 [116] +worker1 [117] +worker2 [117] +worker1 [118] +worker2 [118] +worker1 [119] +worker2 [119] +worker1 [120] +worker2 [120] +worker1 [121] +worker2 [121] +worker1 [122] +worker2 [122] +worker1 [123] +worker2 [123] +worker1 [124] +worker2 [124] +worker1 [125] +worker2 [125] +worker1 [126] +worker2 [126] +worker1 [127] +worker2 [127] +worker1 [128] +worker2 [128] +worker1 [129] +worker2 [129] +worker1 [130] +worker2 [130] +worker1 [131] +worker2 [131] +worker1 [132] +worker2 [132] +worker1 [133] +worker2 [133] +worker1 [134] +worker2 [134] +worker1 [135] +worker2 [135] +worker1 [136] +worker2 [136] +worker1 [137] +worker2 [137] +worker1 [138] +worker2 [138] +worker1 [139] +worker2 [139] +worker1 [140] +worker2 [140] +worker1 [141] +worker2 [141] +worker1 [142] +worker2 [142] +worker1 [143] +worker2 [143] +worker1 [144] +worker2 [144] +worker1 [145] +worker2 [145] +worker1 [146] +worker2 [146] +worker1 [147] +worker2 [147] +worker1 [148] +worker2 [148] +worker1 [149] +worker2 [149] +worker1 [150] +worker2 [150] +worker1 [151] +worker2 [151] +worker1 [152] +worker2 [152] +worker1 [153] +worker2 [153] +worker1 [154] +worker2 [154] +worker1 [155] +worker2 [155] +worker1 [156] +worker2 [156] +worker1 [157] +worker2 [157] +worker1 [158] +worker2 [158] +worker1 [159] +worker2 [159] +worker1 [160] +worker2 [160] +worker1 [161] +worker2 [161] +worker1 [162] +worker2 [162] +worker1 [163] +worker2 [163] +worker1 [164] +worker2 [164] +worker1 [165] +worker2 [165] +worker1 [166] +worker2 [166] +worker1 [167] +worker2 [167] +worker1 [168] +worker2 [168] +worker1 [169] +worker2 [169] +worker1 [170] +worker2 [170] +worker1 [171] +worker2 [171] +worker1 [172] +worker2 [172] +worker1 [173] +worker2 [173] +worker1 [174] +worker2 [174] +worker1 [175] +worker2 [175] +worker1 [176] +worker2 [176] +worker1 [177] +worker2 [177] +worker1 [178] +worker2 [178] +worker1 [179] +worker2 [179] +worker1 [180] +worker2 [180] +worker1 [181] +worker2 [181] +worker1 [182] +worker2 [182] +worker1 [183] +worker2 [183] +worker1 [184] +worker2 [184] +worker1 [185] +worker2 [185] +worker1 [186] +worker2 [186] +worker1 [187] +worker2 [187] +worker1 [188] +worker2 [188] +worker1 [189] +worker2 [189] +worker1 [190] +worker2 [190] +worker1 [191] +worker2 [191] +worker1 [192] +worker2 [192] +worker1 [193] +worker2 [193] +worker1 [194] +worker2 [194] +worker1 [195] +worker2 [195] +worker1 [196] +worker2 [196] +worker1 [197] +worker2 [197] +worker1 [198] +worker2 [198] +worker1 [199] +worker2 [199] +worker1 [200] +worker2 [200] +worker1 [201] +worker2 [201] +worker1 [202] +worker2 [202] +worker1 [203] +worker2 [203] +worker1 [204] +worker2 [204] +worker1 [205] +worker2 [205] +worker1 [206] +worker2 [206] +worker1 [207] +worker2 [207] +worker1 [208] +worker2 [208] +worker1 [209] +worker2 [209] +worker1 [210] +worker2 [210] +worker1 [211] +worker2 [211] +worker1 [212] +worker2 [212] +worker1 [213] +worker2 [213] +worker1 [214] +worker2 [214] +worker1 [215] +worker2 [215] +worker1 [216] +worker2 [216] +worker1 [217] +worker2 [217] +worker1 [218] +worker2 [218] +worker1 [219] +worker2 [219] +worker1 [220] +worker2 [220] +worker1 [221] +worker2 [221] +worker1 [222] +worker2 [222] +worker1 [223] +worker2 [223] +worker1 [224] +worker2 [224] +worker1 [225] +worker2 [225] +worker1 [226] +worker2 [226] +worker1 [227] +worker2 [227] +worker1 [228] +worker2 [228] +worker1 [229] +worker2 [229] +worker1 [230] +worker2 [230] +worker1 [231] +worker2 [231] +worker1 [232] +worker2 [232] +worker1 [233] +worker2 [233] +worker1 [234] +worker2 [234] +worker1 [235] +worker2 [235] +worker1 [236] +worker2 [236] +worker1 [237] +worker2 [237] +worker1 [238] +worker2 [238] +worker1 [239] +worker2 [239] +worker1 [240] +worker2 [240] +worker1 [241] +worker2 [241] +worker1 [242] +worker2 [242] +worker1 [243] +worker2 [243] +worker1 [244] +worker2 [244] +worker1 [245] +worker2 [245] +worker1 [246] +worker2 [246] +worker1 [247] +worker2 [247] +worker1 [248] +worker2 [248] +worker1 [249] +worker2 [249] +worker1 [250] +worker2 [250] +worker1 [251] +worker2 [251] +worker1 [252] +worker2 [252] +worker1 [253] +worker2 [253] +worker1 [254] +worker2 [254] +worker1 [255] +worker2 [255] +worker1 [256] +worker2 [256] +worker2 ok! +worker1 ok! +WaitQ ok! +``` + +> 可以很明显看到加了`WaitQ`之后`worker1`和`worker2`的调度更**均匀**了,或许说均匀并不好,应该说是`worker2: nothing to do!`基本消失了. + +# 块设备 + +引入块设备之后引入了从块设备去加载磁盘的数据.而不是只读取`PFlash`这种简单的设备. + +`AllDevices`管理系统上所有的设备. + +`static`用泛型的方法**高效率**地对设备类型进行的封装.因此**一个类型只能管理一个设备**. + +用`dyn`的动态方法,其实是利用了**动态可变**的`Vec`每个类型有多个**实例**. + +设备的发现方式: +1. 用`pcie`的协议来发现设备 +2. `mmio`通过设备树找设备 + +但是现在没有使用设备树.而是使用**宏**(for_each_drivers!)的方式. + + `virtio`设备是用`qemu`的命令设置出来的虚拟设备. + +有八个槽位来放这些驱动. + +`virtio-mmio`驱动是对各个槽位发送**查询请求**,就此获得槽位的**设备类型**. + +`IPI`中断是多核之间通信的中断,是*软*中断但是是通过发送一个命令到另一个核触发硬中断实现的. + +文件系统的操作流程,就是从`Root`节点一直进行`lookup`的方式一直到目标节点,然后对目标节点进行操作. + +文件系统的示例: +1. `Ext2` +2. `ArceOS`使用的文件系统是`Fat32` + +`mount`可以看成文件系统在内存中的展开操作. + +就是目录树存在储存介质里的时候是扁平的,而现在要使用的是展开的立体结构. + +`mount`可以把另一个文件系统的目录树挂载到当前文件系统的目录树上. + +用`mount`做挂载的时候,可以通过读取最长的路径来解决一部分问题. + +## 读取块设备的实验 + +`make run A=tour/u_7_0 BLK=y` + +> 有一个`feature`叫做`dyn`,只有在开启这个的时候才是**每种类型的设备**有一个`Vec`来管理,不然都是**每种类型的设备**都只有一个. + +> 这里用的驱动不是`mmio`而是`pci`的驱动,虽然PPT上讲了很多关于`mmio`的内容.这令人感到奇怪,看`modules/axdriver/Cargo.toml`里的描述,`default=["bus-pci"]`,这里我们改成`default = ["bus-mmio"]`也可以正常运行,而且看LOG是不一样的. + +> 用`cargo b -vv`在编译的时候看`build.rs`的输出. + +> 同样地,这个实验里被预先加载进**块设备**的内容在`scripts/make/utils.mk`里有定义,详见之前的`mk_pflash`的部分. + + +## 从文件系统加载数据的实验 + +```shell +make run A=tour/u_8_0 BLK=y +``` + +这个任务没什么好说的,主要看这个实验的`Cargo`文件,为`axstd`打开了`fs`这个`feature`. + +我们可以进去尽情看一下实验中调用的`open`和`read`的实现. + +# 为shell增加文件操作命令 + +> 让我难受了很久的一个问题就是这个`rename`,`ax_rename`对应的`(ax_)fatfs`的`rename`也是只能使用**改名当前目录下的文件**. +> 实际上调用的`rust-fatfs`的`rename`是支持改名一个文件到另一个目录下边的.实际上也就是允许把一个`inode`更替到另一个`inode`下边. +> 这样实际上就能用`rename`实现`mv`的功能. + +这一个部分心太急了,没有看其它的`api`的运行流程. + +> 在编译运行的时候应该利用好`LOG="level"` diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\271\235\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\271\235\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..9b14ccbdc87 --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\271\235\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,13 @@ +# 概念 + +1. 虚拟时钟中断支持;虚拟机外设的支持。 +2. 虚拟机中运行宏内核和通过透传pflash加载应用。 + +# 实验 + +> `H_3_0`和`H_4_0`还没有上传做不了基础实验. + +# 课后作业 + +> 可以尝试跑`rcore-ch1`群里有大佬已经实现了. + diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\272\214\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\272\214\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..3c2c4931fdf --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\272\214\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,836 @@ +# 学习内容 + +介绍地址空间和页表相关的内容.任务调度下次课说. + +1. 如何在一个主任务的基础上启用一个子任务,让他完成一系列的工作 +2. 启用一个单任务的基础上能够开两个任务,然后完成两个任务之间的通信 + +# makefile的原理 + +调用是这样实现的: + +首先是`Makefile`里的: +```makefile +run: build justrun +``` + +它需要有`build`和`justrun`这两个**虚拟文件**. + +再去看`build`: +```makefile +build: $(OUT_DIR) $(OUT_BIN) +``` + +需要的是`OUT_DIR`和`OUT_BIN`这两个**实体文件**. + +创建它们两个的文件在`scripts/make/build.mk`: +```makefile +$(OUT_DIR): + $(call run_cmd,mkdir,-p $@) + +$(OUT_BIN): _cargo_build $(OUT_ELF) + $(call run_cmd,$(OBJCOPY),$(OUT_ELF) --strip-all -O binary $@) +``` + +这里调用的`run_cmd`在`scripts/make/utils.mk`: +```makefile +GREEN_C := \033[92;1m +CYAN_C := \033[96;1m +YELLOW_C := \033[93;1m +GRAY_C := \033[90m +WHITE_C := \033[37m +END_C := \033[0m + +define run_cmd + @printf '$(WHITE_C)$(1)$(END_C) $(GRAY_C)$(2)$(END_C)\n' + @$(1) $(2) +endef +``` + +这里`$(1)`和`$(2)`表示接受的是**两个参数**. + +这个是两个操作, +1. 通过**颜色参数**把要执行的命令输出出来(第一行) +2. 第二行相当于执行接受的两个参数 +3. `$@`是代表这个**虚拟文件**本身 + +其实这一套操作下来就是创建这个`OUT_DIR`这个名字的文件夹. + +在`Makefile`中: +```makefile +A ?= tour/u_1_0 +APP ?= $(A) +... ... + +# Paths +OUT_DIR ?= $(APP) +``` + +这时候把目光转回`OUT_BIN`.它在`scripts/make/build.mk`中: +```makefile +$(OUT_BIN): _cargo_build $(OUT_ELF) + $(call run_cmd,$(OBJCOPY),$(OUT_ELF) --strip-all -O binary $@) +``` + +那么它的构建需要**虚拟文件**`_cargo_build`和实体文件`OUT_ELF`. + +那么`_cargo_build`的功能也是先**进行输出**,随后调用`cargo_build`: +```makefile +_cargo_build: + @printf " $(GREEN_C)Building$(END_C) App: $(APP_NAME), Arch: $(ARCH), Platform: $(PLATFORM_NAME), App type: $(APP_TYPE)\n" +ifeq ($(APP_TYPE), rust) + $(call cargo_build,$(APP),$(AX_FEAT) $(LIB_FEAT) $(APP_FEAT)) + @cp $(rust_elf) $(OUT_ELF) +else ifeq ($(APP_TYPE), c) + $(call cargo_build,ulib/axlibc,$(AX_FEAT) $(LIB_FEAT)) +endif +``` + +那么`cargo_build`在`scripts/make/cargo.mk`里: +```makefile +define cargo_build + $(call run_cmd,cargo -C $(1) build,$(build_args) --features "$(strip $(2))") +endef +``` + +由于我们知道`run_cmd`是什么套路了,因此这边就是执行`cargo`来构建一个`elf`文件. + +回到`OUT_BIN`这边,得到两个所需文件之后,通过`OBJCOPY ?= rust-objcopy --binary-architecture=$(ARCH)`(在`Makefile`)中定义,把`elf`多余的信息头去掉,只留下**可执行二进制文件**: +```makefile +$(OUT_BIN): _cargo_build $(OUT_ELF) + $(call run_cmd,$(OBJCOPY),$(OUT_ELF) --strip-all -O binary $@) +``` + +最后回到`justrun`,它调用了`run_qemu`(在`scripts/make/qemu.mk`), +```makefile +define run_qemu + @printf " $(CYAN_C)Running$(END_C) on qemu...\n" + $(call run_cmd,$(QEMU),$(qemu_args-y)) +endef +``` + +这里调用了`QEMU`,是依赖于`ARCH ?= riscv64`(在`Makefile`): +```makefile +QEMU := qemu-system-$(ARCH) +``` + +这里调用了`qemu_args-y`,同样是依赖于`ARCH`的这里不赘述: +```makefile +qemu_args-x86_64 := \ + -machine q35 \ + -kernel $(OUT_ELF) + +qemu_args-riscv64 := \ + -machine virt \ + -bios default \ + -kernel $(OUT_BIN) + +qemu_args-aarch64 := \ + -cpu cortex-a72 \ + -machine virt \ + -kernel $(OUT_BIN) + +qemu_args-y := -m 128M -smp $(SMP) $(qemu_args-$(ARCH)) +``` + + +# ReadPFlash + +本节目标: +1. 引入页表管理组件,通过地址空间重映射,支持设备MMIO +2. 地址空间概念,重映射的意义,页表机制 + +希望能从`PFlash`把应用的数据加载进来,以为运行后边的程序做基础. + +## 实验没有`paging`时的情况 + +### 正常运行 + +```shell +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x0000000087000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 + + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = warn + +Try to access dev region [0xFFFFFFC022000000], got 0x646C6670 +Got pflash magic: pfld +``` + +> `pfld`在哪? + +在`scripts/make/utils.mk`中,在`pflash`中写入了`pfld`: + +```makefile +define mk_pflash + @RUSTFLAGS="" cargo build -p origin --target riscv64gc-unknown-none-elf --release + @rust-objcopy --binary-architecture=riscv64 --strip-all -O binary ./target/riscv64gc-unknown-none-elf/release/origin /tmp/origin.bin + @printf "pfld\00\00\00\01" > /tmp/prefix.bin + @printf "%08x" `stat -c "%s" /tmp/origin.bin` | xxd -r -ps > /tmp/size.bin + @cat /tmp/prefix.bin /tmp/size.bin > /tmp/head.bin + @dd if=/dev/zero of=./$(1) bs=1M count=32 + @dd if=/tmp/head.bin of=./$(1) conv=notrunc + @dd if=/tmp/origin.bin of=./$(1) seek=16 obs=1 conv=notrunc +endef +``` + +那么这个在哪里调用呢?答案是**没有调用**. + +我们是直接`pull`下来的,如果调用`make pflash_img`就会**重新生成**它. + +在`scripts/make/qemu.mk`里: +```makefile +qemu_args-y := -m 128M -smp $(SMP) $(qemu_args-$(ARCH)) + +qemu_args-$(PFLASH) += \ + -drive if=pflash,file=$(CURDIR)/$(PFLASH_IMG),format=raw,unit=1 +... ... + +define run_qemu + @printf " $(CYAN_C)Running$(END_C) on qemu...\n" + $(call run_cmd,$(QEMU),$(qemu_args-y)) +endef +``` + +而在`Makefile`里`PFLASH`被指定为`y`.这样就会在运行`qemu`的时候加上`pflash.img`这个文件. + +### 没有指定`paging`的情况 + +代码: +```rust +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] + +#[macro_use] +#[cfg(feature = "axstd")] +extern crate axstd as std; + +use core::{mem, str}; +use std::os::arceos::modules::axhal::mem::phys_to_virt; + +/// Physical address for pflash#1 +const PFLASH_START: usize = 0x2200_0000; + +#[cfg_attr(feature = "axstd", no_mangle)] +fn main() { + // Makesure that we can access pflash region. + let va = phys_to_virt(PFLASH_START.into()).as_usize(); + let ptr = va as *const u32; + unsafe { + println!("Try to access dev region [{:#X}], got {:#X}", va, *ptr); + let magic = mem::transmute::(*ptr); + println!("Got pflash magic: {}", str::from_utf8(&magic).unwrap()); + } +} + +``` + +这里需要看下一节关于`PFlash`的部分,因为这时候需要访问外设,在没有`remap`的时候是`1G`的**恒等映射**,外设没有映射到地址空间中,因此报错. + +一开始只映射了物理空间的`0x8000_0000`到`0xC000_0000`. + +这里访问的物理地址是`0x2200_0000`,本来就不属于刚刚提到的物理空间,因此通过恒等映射平移也得不到恒等映射之后的虚拟地址. + +产生的log: + +```shell +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x0000000087000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 + + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = warn + +Try to access dev region [0xFFFFFFC022000000], got [ 2.002842 0 axruntime::lang_items:5] panicked at modules/axhal/src/arch/riscv/trap.rs:55:13: +Unhandled trap Exception(LoadFault) @ 0xffffffc080203fa6: +TrapFrame { + regs: GeneralRegisters { + ra: 0xffffffc08020376c, + sp: 0xffffffc0802499f0, + gp: 0x0, + tp: 0x0, + t0: 0x3f, + t1: 0x23, + t2: 0x5, + s0: 0xffffffc080249a80, + s1: 0xffffffc080249c60, + a0: 0xffffffc022000000, + a1: 0xffffffc080249a80, + a2: 0x0, + a3: 0xffffffc080203f9e, + a4: 0x2, + a5: 0xffffffc080202b76, + a6: 0xa, + a7: 0x1, + s2: 0x2, + s3: 0xffffffc080249bc0, + s4: 0xffffffc080249bf0, + s5: 0x38, + s6: 0xffffffc080205098, + s7: 0x2, + s8: 0x1, + s9: 0x24000, + s10: 0x2, + s11: 0xffffffc08026e000, + t3: 0x23, + t4: 0x3a, + t5: 0x5000, + t6: 0x55555555, + }, + sepc: 0xffffffc080203fa6, + sstatus: 0x8000000000006100, +} +``` + +> 分支名称:`tour_u_3_0_no_paging` + +## MAP和REMAP + +ArceOS Unikernel包括两阶段地址空间映射, +Boot阶段默认开启1G空间的恒等映射; +如果需要支持设备MMIO区间,通过指定一个feature - "paging"来实现重映射。 + +上一节说了启动之后需要`remap`,这样才可以实现重映射. + +那么就需要打开`paging`. + +### 初始化的线性页表 + +其实是创建了两个映射,相当于拿前一个恒等映射做了跳板,因为要求开启MMU之后仍然可以以原来的物理地址正常访问. + +`#TODO` + +### 后续创建多级页表 + +`#TODO` + +## PFlash + +是一个模拟闪存磁盘.`QEMU`启动的时候会自动从内存中加载内容到固定的`MMIO`区域. + +读操作是不需要驱动的,但是写是需要驱动的. + +**目前我们只需要读**,只要加载成功即可. + +## 物理地址空间 + +![](00%20inbox/asset/Pasted%20image%2020241117165919.png) + +外设被**映射**到一个物理地址空间里边. + +> 注意:linker_riscv64-qemu-virt.lds,段布局都是听的它的. + +## 分页 + +类似于`rCore`的三级页表,我们实验中用的也是`SV39`. + +![](00%20inbox/asset/Pasted%20image%2020241117170157.png) + +## 分页阶段1-恒等映射+偏移 + +我们希望`sbi`和`kernel`都保存在高地址空间. + +1. 开局的时候把虚拟空间内的地址和物理空间内的地址完全对应上. +2. 给指针寄存器`pc`栈寄存器`sp`加偏移,这里的偏移是`0xffff_ffc0_0000_0000`. + +> `sbi`还是放在`0x8000_0000`,`kernel`还是放在`0x8020_0000`. + +那么在这个情况下其实已经是物理地址了,就是一个**线性偏移**的操作实现虚拟地址和物理地址的映射. + +>如果不需要访问物理设备,现在就可以完成了. + +## 分页阶段2-重建映射 + +重映射的时候干脆在虚拟地址空间里把`sbi`去掉,因为不应该继续访问`sbi`了. + +不同的数据段的权限不一样,比如`READ`,`WRITE`,`EXECUTE`不一样.比如代码段就只能读和运行不能写.在重建的时候就不需要给它这些权限. + +这样设备的地址空间也可以被映射进来,权限粒度也更细. + +![](00%20inbox/asset/Pasted%20image%2020241117204636.png) + + +> 这里似乎仍然是**线性映射**. + +# 多任务 + +需求:启用多任务,开一个子任务,让子任务替主任务完成一些具体工作,然后回到主任务. + +> **并发**是多个任务同时在等待使用CPU而不是同时运行.**并行**是真的需要同时运行. + +>调度的一个很好的描述:一个是**保存现场**,一个是**任务无感知**. + +## 实验多任务 + +`make run A=tour/u_4_0` + +任务代码解析: +```rust +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] + +#[macro_use] +#[cfg(feature = "axstd")] +extern crate axstd as std; + +use core::{mem, str}; +use std::thread; +use std::os::arceos::modules::axhal::mem::phys_to_virt; + +/// Physical address for pflash#1 +const PFLASH_START: usize = 0x2200_0000; + +#[cfg_attr(feature = "axstd", no_mangle)] +fn main() { + println!("Multi-task is starting ..."); + + let worker = thread::spawn(move || { + println!("Spawned-thread ..."); + + // Makesure that we can access pflash region. + let va = phys_to_virt(PFLASH_START.into()).as_usize(); + let ptr = va as *const u32; + let magic = unsafe { + mem::transmute::(*ptr) + }; + if let Ok(s) = str::from_utf8(&magic) { + println!("Got pflash magic: {s}"); + 0 + } else { + -1 + } + }); + + let ret = worker.join(); + // Makesure that worker has finished its work. + assert_eq!(ret, Ok(0)); + + println!("Multi-task OK!"); +} +``` + +和上一节的内容一样,同样是访问了我们预导入的`pflash.img`的前几个字符`pfld`. + +只不过用了`spawn`的方法生成,并且用`join`的方法等待. + +## 任务的数据结构 + +![](00%20inbox/asset/Pasted%20image%2020241117205945.png) + +> 有一个关键点在于`task_ext`,是任务的拓展属性,是面向**宏内核**和**Hypervisor**的关键. + +## 通用调度框架 + + +![](00%20inbox/asset/Pasted%20image%2020241117210345.png) + +分层,并且实现同样的接口,这样就可以自己决定是什么样的调度机制. + +## 系统默认内置任务 + +- GC: 除main之外的任务(线程)退出后,由gc负责回收清理。 +- IDLE: 当其它所有任务都阻塞时,执行它。对某些arch,wait_for_irqs对应非忙等指令.比如等待中断什么的,而不是忙等,如果发生新的"`event`"(自己取的名)那么就会马上响应. + +# MsgQueue + +本节目标: +1. 任务的切换机制,协作式调度算法 +2. 同步的方式,Mutex的机制 + +# 运行实验 + +执行`make run A=tour/u_5_0`: + +```shell +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x0000000087000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 + + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = warn + +Multi-task is starting ... +Wait for workers to exit ... +worker1 ... +worker1 [0] +worker2 ... +worker2 [0] +worker2: nothing to do! +worker1 [1] +worker2 [1] +worker2: nothing to do! +worker1 [2] +worker2 [2] +worker2: nothing to do! +worker1 [3] +worker2 [3] +worker2: nothing to do! +worker1 [4] +worker2 [4] +worker2: nothing to do! +worker1 [5] +worker2 [5] +worker2: nothing to do! +worker1 [6] +worker2 [6] +worker2: nothing to do! +worker1 [7] +worker2 [7] +worker2: nothing to do! +worker1 [8] +worker2 [8] +worker2: nothing to do! +worker1 [9] +worker2 [9] +worker2: nothing to do! +worker1 [10] +worker2 [10] +worker2: nothing to do! +worker1 [11] +worker2 [11] +worker2: nothing to do! +worker1 [12] +worker2 [12] +worker2: nothing to do! +worker1 [13] +worker2 [13] +worker2: nothing to do! +worker1 [14] +worker2 [14] +worker2: nothing to do! +worker1 [15] +worker2 [15] +worker2: nothing to do! +worker1 [16] +worker2 [16] +worker2: nothing to do! +worker1 [17] +worker2 [17] +worker2: nothing to do! +worker1 [18] +worker2 [18] +worker2: nothing to do! +worker1 [19] +worker2 [19] +worker2: nothing to do! +worker1 [20] +worker2 [20] +worker2: nothing to do! +worker1 [21] +worker2 [21] +worker2: nothing to do! +worker1 [22] +worker2 [22] +worker2: nothing to do! +worker1 [23] +worker2 [23] +worker2: nothing to do! +worker1 [24] +worker2 [24] +worker2: nothing to do! +worker1 [25] +worker2 [25] +worker2: nothing to do! +worker1 [26] +worker2 [26] +worker2: nothing to do! +worker1 [27] +worker2 [27] +worker2: nothing to do! +worker1 [28] +worker2 [28] +worker2: nothing to do! +worker1 [29] +worker2 [29] +worker2: nothing to do! +worker1 [30] +worker2 [30] +worker2: nothing to do! +worker1 [31] +worker2 [31] +worker2: nothing to do! +worker1 [32] +worker2 [32] +worker2: nothing to do! +worker1 [33] +worker2 [33] +worker2: nothing to do! +worker1 [34] +worker2 [34] +worker2: nothing to do! +worker1 [35] +worker2 [35] +worker2: nothing to do! +worker1 [36] +worker2 [36] +worker2: nothing to do! +worker1 [37] +worker2 [37] +worker2: nothing to do! +worker1 [38] +worker2 [38] +worker2: nothing to do! +worker1 [39] +worker2 [39] +worker2: nothing to do! +worker1 [40] +worker2 [40] +worker2: nothing to do! +worker1 [41] +worker2 [41] +worker2: nothing to do! +worker1 [42] +worker2 [42] +worker2: nothing to do! +worker1 [43] +worker2 [43] +worker2: nothing to do! +worker1 [44] +worker2 [44] +worker2: nothing to do! +worker1 [45] +worker2 [45] +worker2: nothing to do! +worker1 [46] +worker2 [46] +worker2: nothing to do! +worker1 [47] +worker2 [47] +worker2: nothing to do! +worker1 [48] +worker2 [48] +worker2: nothing to do! +worker1 [49] +worker2 [49] +worker2: nothing to do! +worker1 [50] +worker2 [50] +worker2: nothing to do! +worker1 [51] +worker2 [51] +worker2: nothing to do! +worker1 [52] +worker2 [52] +worker2: nothing to do! +worker1 [53] +worker2 [53] +worker2: nothing to do! +worker1 [54] +worker2 [54] +worker2: nothing to do! +worker1 [55] +worker2 [55] +worker2: nothing to do! +worker1 [56] +worker2 [56] +worker2: nothing to do! +worker1 [57] +worker2 [57] +worker2: nothing to do! +worker1 [58] +worker2 [58] +worker2: nothing to do! +worker1 [59] +worker2 [59] +worker2: nothing to do! +worker1 [60] +worker2 [60] +worker2: nothing to do! +worker1 [61] +worker2 [61] +worker2: nothing to do! +worker1 [62] +worker2 [62] +worker2: nothing to do! +worker1 [63] +worker2 [63] +worker2: nothing to do! +worker1 [64] +worker2 [64] +worker2 ok! +worker1 ok! +Multi-task OK! +``` + +分析代码,`worker1`是尝试获取`Arc`中的这个**双端队列**,然后尝试在队列的最后放东西. + +由于是协作式调度,`worker1`每次放入的之后都会`yield`,因此`worker2`就会接手,然后尝试把队列里**所有的内容**都打出来,如果队列为空就报告`worker2: nothing to do!`,然后再由`worker1`接手CPU. + +## 协作式调度 + +与`rCore`不同,现在使用的是`List`而不是一个数组,原理上就不设置任务的个数了. + +## 互斥锁和自旋锁 + +自旋锁可能是我们脑子中的那个锁,每次访问资源需要访问这个锁,如果没办法访问那你这个任务要处理这种情况. + +互斥锁则是自己加了一个等待队列,如果有任务在等待这个资源,那么这个任务被加入等待队列之后**不会参与调度**,这样就节省了很多任务切换时的资源. + +![](00%20inbox/asset/Pasted%20image%2020241117212636.png) + +# bump内存分配算法 + +`#TODO` + +# 挑战性作业 + +initialize global allocator at: [0xffffffc08026f000, 0xffffffc088000000) + +5376*2 + +align是8的是有关于Vec的内存的分配 + +131,665,920 + +1048576 + +524288+8000 + +`#TODO` + + diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\272\224\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\272\224\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..77bda524ac1 --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\344\272\224\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,309 @@ +# 实验1 + +这部分实验是上部分的作业暂时略过. + +# LinuxApp + +实验命令: +```shell +make payload +./update_disk.sh payload/hello_c/hello +make run A=tour/m_3_0 BLK=y +``` + +这里看`payload/hello_c/Makefile`,可以看到: +```makefile +TARGET := hello + +CC := riscv64-linux-musl-gcc +STRIP := riscv64-linux-musl-strip + +all: $(TARGET) + +%: %.c + $(CC) -static $< -o $@ + $(STRIP) $@ + +clean: + @rm -rf ./$(TARGET) + +``` + +可以看到我们使用的**编译器**和**信息移除工具**都是指定的版本是`linux`. + +这张图有些**害人匪浅**了, + +![](00%20inbox/asset/Pasted%20image%2020241203211933.png) + +这个图是`linux`应用的用户栈. + +但是我们从**实用的角度**来看,应用主函数的原型: +```C +int main(int argc, char *argv[], char *enp[]); +``` + +我们只需要在栈里边按顺序保存: +1. argc +2. arg_ptr +3. env_ptr +4. auxv + +即可,只要`argc`的值是对的,`arg_ptr`和`env_ptr`指向的实例是对的即可. + +> 这里有一个疑问:到底谁是对的? + +在`kernel-elf-parser`里的`src/user_stack.rs`的注释和它具体的实现是一样的: +```rust +//! Initialize the user stack for the application +//! +//! The structure of the user stack is described in the following figure: +//! position content size (bytes) + comment +//! ------------------------------------------------------------------------ +//! stack pointer -> [ argc = number of args ] 8 +//! [ argv[0] (pointer) ] 8 (program name) +//! [ argv[1] (pointer) ] 8 +//! [ argv[..] (pointer) ] 8 * x +//! [ argv[n - 1] (pointer) ] 8 +//! [ argv[n] (pointer) ] 8 (= NULL) +//! [ envp[0] (pointer) ] 8 +//! [ envp[1] (pointer) ] 8 +//! [ envp[..] (pointer) ] 8 +//! [ envp[term] (pointer) ] 8 (= NULL) +//! [ auxv[0] (Elf32_auxv_t) ] 16 +//! [ auxv[1] (Elf32_auxv_t) ] 16 +//! [ auxv[..] (Elf32_auxv_t) ] 16 +//! [ auxv[term] (Elf32_auxv_t) ] 16 (= AT_NULL vector) +//! [ padding ] 0 - 16 +//! [ argument ASCIIZ strings ] >= 0 +//! [ environment ASCIIZ str. ] >= 0 +//! +//! (0xbffffff8) [ end marker ] 8 (= NULL) +//! +//! (0xc0000000) < bottom of stack > 0 (virtual) +//! +//! More details can be found in the link: +``` + +形成的栈: + +![|300](00%20inbox/asset/Pasted%20image%2020241203212752.png) + +运行log: +```shell +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x0000000087000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 + + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = info + +[ 1.746356 0 axruntime:130] Logging is enabled. +[ 1.856119 0 axruntime:131] Primary CPU 0 started, dtb = 0x87000000. +[ 1.905723 0 axruntime:133] Found physcial memory regions: +[ 1.962960 0 axruntime:135] [PA:0x80200000, PA:0x80232000) .text (READ | EXECUTE | RESERVED) +[ 2.026512 0 axruntime:135] [PA:0x80232000, PA:0x80241000) .rodata (READ | RESERVED) +[ 2.073912 0 axruntime:135] [PA:0x80241000, PA:0x80244000) .data .tdata .tbss .percpu (READ | WRITE | RESERVED) +[ 2.124278 0 axruntime:135] [PA:0x80244000, PA:0x80284000) boot stack (READ | WRITE | RESERVED) +[ 2.168556 0 axruntime:135] [PA:0x80284000, PA:0x802ad000) .bss (READ | WRITE | RESERVED) +[ 2.212764 0 axruntime:135] [PA:0x802ad000, PA:0x88000000) free memory (READ | WRITE | FREE) +[ 2.261680 0 axruntime:135] [PA:0x101000, PA:0x102000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 2.305544 0 axruntime:135] [PA:0xc000000, PA:0xc210000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 2.349843 0 axruntime:135] [PA:0x10000000, PA:0x10001000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 2.394978 0 axruntime:135] [PA:0x10001000, PA:0x10009000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 2.440055 0 axruntime:135] [PA:0x22000000, PA:0x24000000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 2.485718 0 axruntime:135] [PA:0x30000000, PA:0x40000000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 2.530990 0 axruntime:135] [PA:0x40000000, PA:0x80000000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 2.583846 0 axruntime:208] Initialize global memory allocator... +[ 2.621634 0 axruntime:209] use TLSF allocator. +[ 2.816195 0 axmm:81] Initialize virtual memory management... +[ 3.188863 0 axruntime:150] Initialize platform devices... +[ 3.249907 0 axtask::api:68] Initialize scheduling... +[ 3.436552 0 axtask::api:74] use Completely Fair scheduler. +[ 3.474966 0 axdriver:152] Initialize device drivers... +[ 3.510394 0 axdriver:153] device model: static +[ 3.664938 0 virtio_drivers::device::blk:59] config: 0xffffffc040006000 +[ 3.721121 0 virtio_drivers::device::blk:64] found a block device of size 65536KB +[ 3.787426 0 axdriver::bus::pci:104] registered a new Block device at 00:01.0: "virtio-blk" +[ 21.285217 0 axfs:41] Initialize filesystems... +[ 21.329601 0 axfs:44] use block device 0: "virtio-blk" +[ 22.099152 0 fatfs::dir:139] Is a directory +[ 22.277106 0 fatfs::dir:139] Is a directory +[ 22.556181 0 fatfs::dir:139] Is a directory +[ 22.683443 0 fatfs::dir:139] Is a directory +[ 22.770783 0 axruntime:176] Initialize interrupt handlers... +[ 22.932112 0 axruntime:186] Primary CPU 0 init OK. +[ 23.210370 0:2 m_3_0::loader:58] e_entry: 0x50E +phdr: offset: 0x0=>0x0 size: 0x17CC=>0x17CC +VA:0x0 - VA:0x2000 +phdr: offset: 0x1E70=>0x2E70 size: 0x338=>0x9A8 +VA:0x2000 - VA:0x4000 +entry: 0x50e +Mapping user stack: VA:0x3fffff0000 -> VA:0x4000000000 +New user address space: AddrSpace { + va_range: VA:0x0..VA:0x4000000000, + page_table_root: PA:0x8064e000, +} +[ 23.946790 0:4 m_3_0::task:56] Enter user space: entry=0x50e, ustack=0x3fffffffc0, kstack=VA:0xffffffc0806a7010 +handle_syscall [96] ... +handle_syscall [29] ... +Unimplemented syscall: SYS_IOCTL +handle_syscall [66] ... +Hello, UserApp! +handle_syscall [66] ... + +handle_syscall [94] ... +[SYS_EXIT_GROUP]: system is exiting .. +monolithic kernel exit [Some(0)] normally! +[ 24.504671 0:2 axhal::platform::riscv64_qemu_virt::misc:3] Shutting down... +``` + + +可以看到运行过程中还调用了:`SYS_IOCTL`和`SYS_SET_TID_ADDRESS`两个系统调用. + +这是因为:"示例m_3_0基于musl工具链以静态方式编译,工具链为应用附加的部分也会调用syscall。" + +![](00%20inbox/asset/Pasted%20image%2020241204001716.png) + +就是添加的这个`_start`和`_exit`的系统调用. + +> `set_tid_address`会设置`clear_child_tid`的值,在进程创建和释放的时候会用到. +> `set_tid_address`在父线程创建一个子线程的时候会把自己的`tid`写到这个`address`的区域里. +> `clear_child_tid`在释放自己线程或者锁和其它资源的时候,会把返回的值里写入到`address`里. + +>`ioctl`是用来设置对外输出终端属性的. +>现在用的是`sbi`的`putchar`,因此可以直接跳过. + +> 对于不同的体系结构,系统调用号不同。示例是基于riscv64的系统调用号规范。 + +最后总结就是我们设置好合理的`syscall`,把系统调用号设置好,那么就可以实现一定程度上的兼容. + +![](00%20inbox/asset/Pasted%20image%2020241204002546.png) + +像这个APP只需要提供`syscall`的兼容层就行了. + +其余的兼容层根据APP不同也需要实现. + +## 对Linux常用文件系统的支持 + +> arceOS是通过`axfs_ramfs`对`procfs`和`sysfs`提供兼容.通过`axfs_devfs`提供`devfs`的兼容. +> 目前用`ramfs`进行兼容是一个临时的方法. +> 也就是使用内存文件系统.访问的时候相当于访问了一个基于内存的节点,里边有一些基于内存的数据,这些数据是其它子系统填充过来的数据. +> 正常的Linux是你访问这个`proc`之类的文件的时候实际上是调用了一个回调函数去获取系统状态. + +# 实现mmap系统调用 + +实现方法: +1. 通过`sys_read`方法读取到文件里的内容. +2. 读取当前的任务的`user space`. +3. 寻找空闲的映射空间的虚拟地址 +4. 构造`flag`. +5. 创建一块`frame`,并且把虚拟地址映射到`frame`. +6. 把文件内容拷贝到内存中去 + +```rust +#[allow(unused_variables)] +fn sys_mmap( + addr: *mut usize, + length: usize, + prot: i32, + flags: i32, + fd: i32, + _offset: isize, +) -> isize { + const MAX_MMAP_SIZE: usize = 64; + let mut buf: [u8; 64] = [0u8;MAX_MMAP_SIZE]; + unsafe { + let buf_ptr = &mut buf as *mut _ as *mut c_void; + sys_read(fd, buf_ptr, length+_offset as usize); + } + let mut buf = &buf[_offset as usize..length+_offset as usize]; + + let binding = current(); + let mut uspace = &mut binding.task_ext().aspace.lock(); + + let free_va = if addr.is_null() { + uspace.find_free_area( + (addr as usize).into(), + length, + VirtAddrRange::new( + uspace.base(), + uspace.end())) + .unwrap_or_else(|| panic!("No free area for mmap")) + }else{ + (addr as usize).into() + }; + + // 把prot转换成MappingFlags + let mut flags = MappingFlags::from(MmapProt::from_bits_truncate(prot)); + flags.set(MappingFlags::USER, true); + + uspace.map_alloc( + free_va, + PAGE_SIZE_4K, + flags, + true) + .unwrap(); + let (paddr, _, _) = uspace + .page_table() + .query(free_va) + .unwrap_or_else(|_| panic!("Mapping failed for segment")); + unsafe { + core::ptr::copy_nonoverlapping( + buf.as_ptr(), + phys_to_virt(paddr).as_mut_ptr(), + PAGE_SIZE_4K, + ); + } + free_va.as_usize() as isize +} +``` + +> 这里`flags`的处理还是很不到位,需要后续增加. \ No newline at end of file diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\205\253\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\205\253\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..6eef92f411a --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\205\253\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,19 @@ +# 内容 + +![](00%20inbox/asset/Pasted%20image%2020241206004959.png) + +虚拟机运行的实验内核是第一周的u_3_0:从pflash设备读出数据,验证开头部分。 + +有两种处理方式: +1. 模拟模式 - 为虚拟机模拟一个pflash,以file1为后备文件。当Guest读该设备时,提供file1文件的内容。 +2. 透传模式 - 直接把宿主物理机(即qemu)的pflash透传给虚拟机。 + +优劣势:模拟模式可为不同虚拟机提供不同的pflash内容,但效率低;透传模式效率高,但是捆绑了设备。 + + +# 实验 + + + +# 课后作业 + diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\205\255\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\205\255\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..8385d65a6b6 --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\205\255\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,31 @@ +# 组件化内核的心得和实践经验 + +应用场景多样化->多种内核场景的出现 + +Unikernel->安全性换高效性->为一个APP适配一个内核 + +宏内核就是典型的Linux之类的操作系统 + +微内核主要是安全->用形式化证明安全性->反复切换用户态以至于很慢 + +虚拟机管理程序->hypervisor->多个内核每个内核认为自己独享了整个设备 + +> 关注点在于组件化场景下的异构内核的快速实现.理解概念和优势. + +**不同的需求对应了不同的内核**->**使用不同的组件实现不同的内核** + +> 使用宏内核+hypervisor的架构也可以实现这个功能,但是会产生性能瓶颈. + + +利用对`unikernel`的几个部件的连接方式的修改,加一个宏内核插件,这样就可以变成宏内核. + +通过对`unikernel`对于`hypervisor`插件的调用,就可以变成`hypervisor`的系统. + +> 其实上边论述的是优势所在. + + +>BACKBONE层的重要性:把共性放在下层内容. +>TASK的拓展:把任务看成是内核资源的集合. + + +>未来工作:扩展泛型化——同时引入不同类型扩展 -> 甚至能到异构内核 diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\233\233\350\212\202\350\257\276\347\254\224\350\256\260.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\233\233\350\212\202\350\257\276\347\254\224\350\256\260.md" new file mode 100644 index 00000000000..b3cf7ebd67e --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil/ArceOS\347\254\254\345\233\233\350\212\202\350\257\276\347\254\224\350\256\260.md" @@ -0,0 +1,317 @@ +# 回顾与展望 + +之前就是做了一系列的实验建立了unikernel的框架. + +通过unikernel的形式通过增加一些组件来跨过这个边界,来实现一个宏内核. + +# 从Unikernel到宏内核 + +通过跨越模式边界,弄一个简单的系统调用的操作. + +> 增加用户特权级和特权级上下文切换是变成宏内核的关键. + +## 实验1 + +> `rust-analyzer`不能正常解析代码的原因,需要在`.vscode/settings.json`里加入`"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf"` + +实验命令行: +```shell +make payload +./update_disk.sh ./payload/origin/origin +make run A=tour/m_1_0 BLK=y +``` + +> 如果不能执行`payload`说明代码版本太老了,需要先`git fetch origin`然后再`git merge origin`到当前的分支 + +> 这里注意如果`make payload`报错`Error`,那么一定是因为没有配置好`musl`的环境变量,注意看一下`~/.bashrc`,记得更新完`~/.bashrc`要进行狠狠的`source ~/.bashrc` + +对于`./update_disk.sh ./payload/origin/origin`的操作对于我这种没操作过的人来说是非常神奇的操作.这一步实际上是把`disk.img`挂载在linux的文件系统里,然后在直接用linux的指令直接往里边拷贝应用文件的数据. + +然后`make run A=tour/m_1_0 BLK=y`就和上一节课的实验一样了. + +跑出来的结果是: +```shell +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x0000000087000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 + + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = warn + +[ 21.794824 0 fatfs::dir:139] Is a directory +[ 22.065035 0 fatfs::dir:139] Is a directory +[ 22.359963 0 fatfs::dir:139] Is a directory +[ 22.490439 0 fatfs::dir:139] Is a directory +app: /sbin/origin +paddr: PA:0x80642000 +Mapping user stack: VA:0x3fffff0000 -> VA:0x4000000000 +New user address space: AddrSpace { + va_range: VA:0x0..VA:0x4000000000, + page_table_root: PA:0x80641000, +} +Enter user space: entry=0x1000, ustack=0x4000000000, kstack=VA:0xffffffc080697010 +handle_syscall ... +[SYS_EXIT]: process is exiting .. +monolithic kernel exit [Some(0)] normally! +``` + +让我们看一下`orgin`的app内容: +```rust +#[no_mangle] +unsafe extern "C" fn _start() -> ! { + core::arch::asm!( + "addi sp, sp, -4", + "sw a0, (sp)", + "li a7, 93", + "ecall", + options(noreturn) + ) +} +``` + +很容易懂的,就是调用了第`93`号`syscall`. + +# 课后练习 + +> 主要是要理解`AddrSpace`在`map_alloc`的时候的`populating`选项. + +根据在`rCore`中学到的经验,去查看源码,我们的结构是这样的. + +![|800](00%20inbox/asset/Pasted%20image%2020241202104723.png) + +就是在创建`MemoryArea`的时候要传入一个泛型`Backend`. + +应该就是和这边页的懒加载有关的内容. + +调用到最后调用的是`modules/axmm/src/backend/alloc.rs`这个文件里的`map_alloc`,因为层层抽象,这里各个参数都还原成了最开始`tour/m_1_0/src/main.rs`里的变量名称. + +![|800](00%20inbox/asset/Pasted%20image%2020241202211802.png) + +然后关键代码是: +```rust + if populate { + // allocate all possible physical frames for populated mapping. + for addr in PageIter4K::new(start, start + size).unwrap() { + if let Some(frame) = alloc_frame(true) { + if let Ok(tlb) = pt.map(addr, frame, PageSize::Size4K, flags) { + tlb.ignore(); // TLB flush on map is unnecessary, as there are no outdated mappings. + } else { + return false; + } + } + } + true + } else { + // Map to a empty entry for on-demand mapping. + let flags = MappingFlags::empty(); + pt.map_region(start, |_| 0.into(), size, flags, false, false) + .map(|tlb| tlb.ignore()) + .is_ok() + } +``` + +这里假如我们的`poplulate`是选定的`true`,那么就会立即根据`4k`一个大小的`frame`进行内存申请,然后把这个虚拟地址和刚刚申请到的`frame`在`page_table`中映射起来. + +但是如果我们选定`populate`为`false`,那么直接把虚拟地址和`0`这个错误的物理地址映射起来. + +那么这时候实际上就需要我们在访问到这个物理地址的时候,**再进行物理页申请**. + +那么在访问到这个地址的时候会发生**缺页异常**. + +这时候我们运行一下应用: +```shell +make payload +./update_disk.sh payload/origin/origin +make run A=tour/m_1_0/ BLK=y +``` + +这是对应的`log`: +```shell +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x0000000087000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 + + d8888 .d88888b. .d8888b. + d88888 d88P" "Y88b d88P Y88b + d88P888 888 888 Y88b. + d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b. + d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b. + d88P 888 888 888 88888888 888 888 "888 + d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P +d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P" + +arch = riscv64 +platform = riscv64-qemu-virt +target = riscv64gc-unknown-none-elf +smp = 1 +build_mode = release +log_level = warn + +[ 21.690418 0 fatfs::dir:139] Is a directory +[ 21.963457 0 fatfs::dir:139] Is a directory +[ 22.252957 0 fatfs::dir:139] Is a directory +[ 22.383790 0 fatfs::dir:139] Is a directory +app: /sbin/origin +paddr: PA:0x80642000 +Mapping user stack: VA:0x3fffff0000 -> VA:0x4000000000 +New user address space: AddrSpace { + va_range: VA:0x0..VA:0x4000000000, + page_table_root: PA:0x80641000, +} +Enter user space: entry=0x1000, ustack=0x4000000000, kstack=VA:0xffffffc080687010 +[ 23.235085 0:4 axhal::arch::riscv::trap:24] No registered handler for trap PAGE_FAULT +[ 23.319751 0:4 axruntime::lang_items:5] panicked at modules/axhal/src/arch/riscv/trap.rs:25:9: +Unhandled User Page Fault @ 0x1002, fault_vaddr=VA:0x3ffffffffc (WRITE | USER): +TrapFrame { + regs: GeneralRegisters { + ra: 0x0, + sp: 0x3ffffffffc, + gp: 0x0, + tp: 0x0, + t0: 0x0, + t1: 0x0, + t2: 0x0, + s0: 0x0, + s1: 0x0, + a0: 0x0, + a1: 0x0, + a2: 0x0, + a3: 0x0, + a4: 0x0, + a5: 0x0, + a6: 0x0, + a7: 0x0, + s2: 0x0, + s3: 0x0, + s4: 0x0, + s5: 0x0, + s6: 0x0, + s7: 0x0, + s8: 0x0, + s9: 0x0, + s10: 0x0, + s11: 0x0, + t3: 0x0, + t4: 0x0, + t5: 0x0, + t6: 0x0, + }, + sepc: 0x1002, + sstatus: 0x40020, +} +``` + +实现方法`tour/m_2_0`里的实现: +```rust +#[register_trap_handler(PAGE_FAULT)] +fn handle_page_fault(vaddr: VirtAddr, access_flags: MappingFlags, is_user: bool) -> bool { + if is_user { + if !axtask::current() + .task_ext() + .aspace + .lock() + .handle_page_fault(vaddr, access_flags) + { + ax_println!("{}: segmentation fault, exit!", axtask::current().id_name()); + axtask::exit(-1); + } else { + ax_println!("{}: handle page fault OK!", axtask::current().id_name()); + } + true + } else { + false + } +} +``` + +这里主要是调用了`aspace`也即当前任务地址空间中处理缺页故障的方法. + +就像我们之前在上一节分析到的`Backend`的`map`方法一样,还是调用了`Backend`的`remap`方法. + +就是当即分配一个`frame`,然后把当前出问题的`va`虚拟地址重新映射到`frame`. + + +