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/2024\347\247\213\345\206\254\345\255\243\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\351\241\271\347\233\2561\346\226\271\345\220\2212\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\347\247\213\345\206\254\345\255\243\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\351\241\271\347\233\2561\346\226\271\345\220\2212\346\200\273\347\273\223-winddevil.md" new file mode 100644 index 00000000000..c5ede1a40a9 --- /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/2024\347\247\213\345\206\254\345\255\243\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\351\241\271\347\233\2561\346\226\271\345\220\2212\346\200\273\347\273\223-winddevil.md" @@ -0,0 +1,21 @@ +# 详细编写过程 + +附在ArceOS igb网卡驱动编写上 + +# 体会 + +前边很多时间用在流控、过滤器上边。由于igb-drive和ixgbe-drive的抽象化不一样。尤其是对ring和dma内存的结构的抽象。 + +比如把ring初始化的时候的操作,需要适配Tx和Rx。看文档的时间花了很久,具体怎么抽象化反而做的很差。 + +最后igb-drive卡在Descriptor构成之后想要写入Tail发送,但是想不出怎么进行发送上边。 + +很难解决具体的问题。 + +后来看到群友有人魔改ixgbe-dirve,得到成功。进而参考学习,自己也可以通过修改本地的ixgbe驱动来实现成功的httpserver。 + +并且看到有的人参考的驱动是旧版本的linux驱动,而我参考的是新版本的驱动,这就体现了我策略性的问题。 + +# 后续 + +不管这次结果如何,仍然想要参加下一期的学习,并且尝试自己完成igb驱动的编写,并且放到自己的blog中去。 \ 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 igb\347\275\221\345\215\241\351\251\261\345\212\250\347\274\226\345\206\231.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 igb\347\275\221\345\215\241\351\251\261\345\212\250\347\274\226\345\206\231.md" new file mode 100644 index 00000000000..f96d5db430d --- /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 igb\347\275\221\345\215\241\351\251\261\345\212\250\347\274\226\345\206\231.md" @@ -0,0 +1,1068 @@ +# qemu 模拟 igb设备 + +## Make方式 + +在linux系统下可使用make方式运行,需修改arceos/scripts/make目录下qemu.mk文件,更改 Qemu 模拟网卡为 igb。 + +将`qemu_args-$(NET)`的参数,由`-device virtio-net-$(vdev-suffix),netdev=net0`修改为`-device igb,netdev=net0`。 + +在arceos目录下运行make命令: + +```shell +make A=examples/httpclient PLATFORM=aarch64-qemu-virt LOG=debug SMP=2 NET=y NET_DEV=user run +``` + +最终生成的运行命令: +```shell +qemu-system-aarch64 -m 128M -smp 2 -cpu cortex-a72 -machine virt -kernel examples/httpclient/httpclient_aarch64-qemu-virt.bin -device virtio-net-pci,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:5555,hostfwd=udp::5555-:5555 -nographic +``` + +这里其实我们可以自己进行一个理解,先从`qemu`的命令开始: +1. `-m` 设置分配的内存 +2. `-smp` 设置对称多处理(SMP)配置,这里指定的是创建一个具有两个 CPU 核心的虚拟机 +3. `-cpu` 指定要模拟的 CPU 类型,在这里是 Cortex-A72 +4. `-machine` 选择平台,这里使用 `virt` 类型的机器模型,这是一个通用的,不与任何特定硬件绑定的虚拟平台 +5. `-kernel` 指定内核映像文件,这个文件是在虚拟机启动时加载的程序或操作系统内核 +6. `-device` 添加一个 `PCI` 设备到虚拟机中,这个设备是一个`VirtIO` 网络适配器,并且它连接到了 `ID` 为 `net0` 的网络后端 +7. `-netdev` 定义了一个用户模式网络后端,其 `ID` 是 `net0`。同时设置了主机端口转发规则,将主机的 `TCP` 和 `UDP` 的 `5555` 端口转发到虚拟机的相同端口 +8. `-nographic` 禁用图形输出 + +那么我们可以得到如下的问题. + +## 如何编译得来`.bin`文件? + +看log的时候很不仔细,其实在log里是有的: + +```shell +cargo -C examples/httpclient build -Z unstable-options --target aarch64-unknown-none-softfloat --target-dir /home/winddevil/workspace/arceos/target --release --features "axstd/log-level-debug axstd/smp" +``` + +然后我们直接运行这个编译过程是发现会报错的,这是什么原因呢?输出log是: + +```shell +warning: `/home/winddevil/.cargo/config` is deprecated in favor of `config.toml` +note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml` +warning: `/home/winddevil/.cargo/config` is deprecated in favor of `config.toml` +note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml` +warning: `/home/winddevil/.cargo/config` is deprecated in favor of `config.toml` +note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml` +warning: `/home/winddevil/.cargo/config` is deprecated in favor of `config.toml` +note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml` + Compiling log v0.4.21 + Compiling cfg-if v1.0.0 + Compiling tock-registers v0.8.1 + Compiling bitflags v2.6.0 + Compiling axerrno v0.1.0 + Compiling byteorder v1.4.3 + Compiling const-default v1.0.0 + Compiling memory_addr v0.3.1 + Compiling bit_field v0.10.2 + Compiling percpu v0.1.3 + Compiling lock_api v0.4.10 + Compiling lazyinit v0.2.1 + Compiling axconfig v0.1.0 (/home/winddevil/workspace/arceos/modules/axconfig) + Compiling int_ratio v0.1.0 + Compiling static_assertions v1.1.0 + Compiling linkme v0.3.27 + Compiling scopeguard v1.2.0 + Compiling handler_table v0.1.1 + Compiling kernel_guard v0.1.1 + Compiling axdriver_base v0.1.0 (https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.0#78686a7e) + Compiling aarch64-cpu v9.4.0 + Compiling rlsf v0.2.1 + Compiling dw_apb_uart v0.1.0 + Compiling arm_gicv2 v0.1.0 + Compiling arm_pl011 v0.1.0 + Compiling bitmap-allocator v0.1.0 + Compiling heapless v0.7.16 + Compiling kspin v0.1.0 + Compiling zerocopy v0.7.35 + Compiling hash32 v0.2.1 + Compiling stable_deref_trait v1.2.0 + Compiling smoltcp v0.10.0 (https://github.com/rcore-os/smoltcp.git?rev=2ade274#2ade2747) + Compiling axdriver v0.1.0 (/home/winddevil/workspace/arceos/modules/axdriver) + Compiling num-traits v0.2.16 + Compiling managed v0.8.0 + Compiling axlog v0.1.0 (/home/winddevil/workspace/arceos/modules/axlog) + Compiling bitflags v1.3.2 + Compiling axio v0.1.0 + Compiling spin v0.9.8 + Compiling allocator v0.1.0 (https://github.com/arceos-org/allocator.git?tag=v0.1.0#16496d88) + Compiling axdriver_net v0.1.0 (https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.0#78686a7e) + Compiling axalloc v0.1.0 (/home/winddevil/workspace/arceos/modules/axalloc) + Compiling virtio-drivers v0.7.4 + Compiling axhal v0.1.0 (/home/winddevil/workspace/arceos/modules/axhal) + Compiling chrono v0.4.38 + Compiling page_table_entry v0.4.0 + Compiling axdriver_virtio v0.1.0 (https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.0#78686a7e) + Compiling axdriver_pci v0.1.0 (https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.0#78686a7e) + Compiling page_table_multiarch v0.4.0 +error[E0425]: cannot find function `init_boot_page_table` in module `crate::platform::mem` + --> modules/axhal/src/platform/aarch64_common/boot.rs:100:27 + | +100 | crate::platform::mem::init_boot_page_table(addr_of_mut!(BOOT_PT_L0), addr_of_mut!(BOOT_PT_L1)); + | ^^^^^^^^^^^^^^^^^^^^ not found in `crate::platform::mem` + +error[E0425]: cannot find value `rust_entry` in module `crate::platform` + --> modules/axhal/src/platform/aarch64_common/boot.rs:139:38 + | +139 | entry = sym crate::platform::rust_entry, + | ^^^^^^^^^^ not found in `crate::platform` + +error[E0425]: cannot find value `rust_entry_secondary` in module `crate::platform` + --> modules/axhal/src/platform/aarch64_common/boot.rs:170:38 + | +170 | entry = sym crate::platform::rust_entry_secondary, + | ^^^^^^^^^^^^^^^^^^^^ not found in `crate::platform` + +error[E0425]: cannot find value `PSCI_METHOD` in crate `axconfig` + --> modules/axhal/src/platform/aarch64_common/psci.rs:82:31 + | +82 | let ret = match axconfig::PSCI_METHOD { + | ^^^^^^^^^^^ not found in `axconfig` + +error[E0425]: cannot find value `PSCI_METHOD` in crate `axconfig` + --> modules/axhal/src/platform/aarch64_common/psci.rs:85:58 + | +85 | _ => panic!("Unknown PSCI method: {}", axconfig::PSCI_METHOD), + | ^^^^^^^^^^^ not found in `axconfig` + +error[E0425]: cannot find value `UART_PADDR` in crate `axconfig` + --> modules/axhal/src/platform/aarch64_common/pl011.rs:9:43 + | +9 | const UART_BASE: PhysAddr = pa!(axconfig::UART_PADDR); + | ^^^^^^^^^^ not found in `axconfig` + +For more information about this error, try `rustc --explain E0425`. +error: could not compile `axhal` (lib) due to 6 previous errors +warning: build failed, waiting for other jobs to finish... +``` + +这里有个非常重要的细节,我们报告的错都是`modules/axhal/src/platform/aarch64_common`这个文件夹里的. + +那么我们发现这个`paltform`不对啊,它怎么不给我编译`aarch64-qemu-virt`呢? + +> 这里要提到一个我们经常会使用到的东西,即查看`make`过程的`V=1`,在`make`指令后边加上这句,就可以看到`make`的过程 + +然后我们看到: +```shell +APP: "examples/httpclient" +APP_TYPE: "rust" +FEATURES: "" +arceos features: "axstd/log-level-debug" +lib features: "axstd/smp" +app features: "" +RUSTFLAGS: "-C link-arg=-T/home/winddevil/workspace/arceos/target/aarch64-unknown-none-softfloat/release/linker_aarch64-qemu-virt.lds -C link-arg=-no-pie -C link-arg=-znostart-stop-gc" +``` + +这里是来自`scripts/make/build.mk`: +```shell +ifneq ($(filter $(MAKECMDGOALS),doc doc_check_missing),) # run `cargo doc` + $(if $(V), $(info RUSTDOCFLAGS: "$(RUSTDOCFLAGS)")) + export RUSTDOCFLAGS +else ifeq ($(filter $(MAKECMDGOALS),clippy unittest unittest_no_fail_fast),) # not run `cargo test` or `cargo clippy` + ifneq ($(V),) + $(info APP: "$(APP)") + $(info APP_TYPE: "$(APP_TYPE)") + $(info FEATURES: "$(FEATURES)") + $(info arceos features: "$(AX_FEAT)") + $(info lib features: "$(LIB_FEAT)") + $(info app features: "$(APP_FEAT)") + endif + ifeq ($(APP_TYPE), c) + $(if $(V), $(info CFLAGS: "$(CFLAGS)") $(info LDFLAGS: "$(LDFLAGS)")) + else + $(if $(V), $(info RUSTFLAGS: "$(RUSTFLAGS)")) + export RUSTFLAGS + endif +endif +``` + +> 这里后来发现两个问题,一个是虽然输出了几个关键的环境变量还是没有输出全部的环境变量还是会导致不能编译 + +这里介绍一个关键字`export`: + +```shell +export [-fnp][变量名称]=[变量设置值] + +参数说明: + +-f  代表[变量名称]中为函数名称。 +-n  删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。 +-p  列出所有的shell赋予程序的环境变量。 +``` + +上述是通过`export`的环境变量. + +那么其中有一个比较可疑的,就是`RUSTFLAGS`,在[这里](https://rustwiki.org/zh-CN/cargo/reference/environment-variables.html): + +> 自定义参数的空格分隔列表,用来传递给 Cargo 执行的所有编译器调用。与`cargo rustc`不同,这对于传递一个标志 _全部的_ 编译实例是有用的。 + +经过多次尝试,设置这两个环境变量即可编译: +```shell +export RUSTFLAGS="-C link-arg=-T/home/winddevil/workspace/arceos/target/aarch64-unknown-none-softfloat/release/linker_aarch64-qemu-virt.lds -C link-arg=-no-pie -C link-arg=-znostart-stop-gc" +export AX_PLATFORM="aarch64-qemu-virt" +cargo -C examples/httpclient build -Z unstable-options --target aarch64-unknown-none-softfloat --target-dir /home/winddevil/workspace/arceos/target --release --features "axstd/log-level-debug axstd/smp" +``` + +这说明了编译流程里是有设置很多环境变量的. + +编译过程很好理解: + +![](00%20inbox/asset/Pasted%20image%2020241208170548.png) + +> 但是仍然没有办法解决在`cargo build`的时候到底有哪些环境变量被`export`了. + +我们搜索`export`,其实也不多: +```makefile +// Makefile + +export AX_ARCH=$(ARCH) +export AX_PLATFORM=$(PLATFORM_NAME) +export AX_SMP=$(SMP) +export AX_MODE=$(MODE) +export AX_LOG=$(LOG) +export AX_TARGET=$(TARGET) +export AX_IP=$(IP) +export AX_GW=$(GW) + +// scripts/make/build.mk + +ifneq ($(filter $(MAKECMDGOALS),doc doc_check_missing),) # run `cargo doc` + $(if $(V), $(info RUSTDOCFLAGS: "$(RUSTDOCFLAGS)")) + export RUSTDOCFLAGS +else ifeq ($(filter $(MAKECMDGOALS),clippy unittest unittest_no_fail_fast),) # not run `cargo test` or `cargo clippy` + ifneq ($(V),) + $(info APP: "$(APP)") + $(info APP_TYPE: "$(APP_TYPE)") + $(info FEATURES: "$(FEATURES)") + $(info arceos features: "$(AX_FEAT)") + $(info lib features: "$(LIB_FEAT)") + $(info app features: "$(APP_FEAT)") + endif + ifeq ($(APP_TYPE), c) + $(if $(V), $(info CFLAGS: "$(CFLAGS)") $(info LDFLAGS: "$(LDFLAGS)")) + else + $(if $(V), $(info RUSTFLAGS: "$(RUSTFLAGS)")) + export RUSTFLAGS + endif +endif +``` + +我们需要看看各个包在编译的时候,`build.rs`是怎么运行的,会加入什么环境变量进去. + +依赖图: + +![](00%20inbox/asset/Pasted%20image%2020241208170513.png) + +这里边最重要的就是`config.rs`,它是由`axconfig`进行编译的时候`build.rs`生成的. + +### `build.rs`解析 + +```rust +use std::io::{Result, Write}; +use std::path::{Path, PathBuf}; +use toml_edit::{Decor, DocumentMut, Item, Table, Value}; + +fn resolve_config_path(platform: Option<&str>) -> Result { + let mut root_dir = PathBuf::from(std::env!("CARGO_MANIFEST_DIR")); + root_dir.extend(["..", ".."]); + let config_dir = root_dir.join("platforms"); + + let builtin_platforms = std::fs::read_dir(&config_dir)? + .filter_map(|e| { + e.unwrap() + .file_name() + .to_str()? + .strip_suffix(".toml") + .map(String::from) + }) + .collect::>(); + + let path = match platform { + None | Some("") => "defconfig.toml".into(), + Some(plat) if builtin_platforms.contains(&plat.to_string()) => { + config_dir.join(format!("{plat}.toml")) + } + Some(plat) => { + let path = PathBuf::from(&plat); + if path.is_absolute() { + path + } else { + root_dir.join(plat) + } + } + }; + + Ok(path) +} + +fn get_comments<'a>(config: &'a Table, key: &str) -> Option<&'a str> { + config + .key(key) + .and_then(|k| k.leaf_decor().prefix()) + .and_then(|s| s.as_str()) + .map(|s| s.trim()) +} + +fn add_config(config: &mut Table, key: &str, item: Item, comments: Option<&str>) { + config.insert(key, item); + if let Some(comm) = comments { + if let Some(mut dst) = config.key_mut(key) { + *dst.leaf_decor_mut() = Decor::new(comm, ""); + } + } +} + +fn load_config_toml(config_path: &Path) -> Result { + let config_content = std::fs::read_to_string(config_path)?; + let toml = config_content + .parse::() + .expect("failed to parse config file") + .as_table() + .clone(); + Ok(toml) +} + +fn gen_config_rs(config_path: &Path) -> Result> { + fn is_num(s: &str) -> bool { + let s = s.replace('_', ""); + if s.parse::().is_ok() { + true + } else if let Some(s) = s.strip_prefix("0x") { + usize::from_str_radix(s, 16).is_ok() + } else { + false + } + } + + // Load TOML config file + let mut config = if config_path == Path::new("defconfig.toml") { + load_config_toml(config_path)? + } else { + // Set default values for missing items + let defconfig = load_config_toml(Path::new("defconfig.toml"))?; + let mut config = load_config_toml(config_path)?; + + for (key, item) in defconfig.iter() { + if !config.contains_key(key) { + add_config( + &mut config, + key, + item.clone(), + get_comments(&defconfig, key), + ); + } + } + config + }; + + add_config( + &mut config, + "smp", + toml_edit::value(std::env::var("AX_SMP").unwrap_or("1".into())), + Some("# Number of CPUs"), + ); + + // Generate config.rs + let mut output = Vec::new(); + writeln!( + output, + "// Platform constants and parameters for {}.", + config["platform"].as_str().unwrap(), + )?; + writeln!(output, "// Generated by build.rs, DO NOT edit!\n")?; + + for (key, item) in config.iter() { + let var_name = key.to_uppercase().replace('-', "_"); + if let Item::Value(value) = item { + let comments = get_comments(&config, key) + .unwrap_or_default() + .replace('#', "///"); + match value { + Value::String(s) => { + writeln!(output, "{comments}")?; + let s = s.value(); + if is_num(s) { + writeln!(output, "pub const {var_name}: usize = {s};")?; + } else { + writeln!(output, "pub const {var_name}: &str = \"{s}\";")?; + } + } + Value::Array(regions) => { + if key != "mmio-regions" && key != "virtio-mmio-regions" && key != "pci-ranges" + { + continue; + } + writeln!(output, "{comments}")?; + writeln!(output, "pub const {var_name}: &[(usize, usize)] = &[")?; + for r in regions.iter() { + let r = r.as_array().unwrap(); + writeln!( + output, + " ({}, {}),", + r.get(0).unwrap().as_str().unwrap(), + r.get(1).unwrap().as_str().unwrap() + )?; + } + writeln!(output, "];")?; + } + _ => {} + } + } + } + + Ok(output) +} + +fn main() -> Result<()> { + let platform = option_env!("AX_PLATFORM"); + let config_path = resolve_config_path(platform)?; + + println!("Reading config file: {:?}", config_path); + let config_rs = gen_config_rs(&config_path)?; + + let out_dir = std::env::var("OUT_DIR").unwrap(); + let out_path = Path::new(&out_dir).join("config.rs"); + println!("Generating config file: {}", out_path.display()); + std::fs::write(out_path, config_rs)?; + + println!("cargo:rerun-if-changed={}", config_path.display()); + println!("cargo:rerun-if-env-changed=AX_PLATFORM"); + println!("cargo:rerun-if-env-changed=AX_SMP"); + Ok(()) +} + +``` + +我们通过在编译过程中加入`-vv`查看更详细的编译过程,即: +```shell +export RUSTFLAGS="-C link-arg=-T/home/winddevil/workspace/arceos/target/aarch64-unknown-none-softfloat/release/linker_aarch64-qemu-virt.lds -C link-arg=-no-pie -C link-arg=-znostart-stop-gc" +export AX_PLATFORM="aarch64-qemu-virt" +cargo -C examples/httpclient build -Z unstable-options --target aarch64-unknown-none-softfloat --target-dir /home/winddevil/workspace/arceos/target --release --features "axstd/log-level-debug axstd/smp" -vv +``` + +可以看到一个好的信息, +```shell +[axconfig 0.1.0] Generating config file: /home/winddevil/workspace/arceos/target/release/build/axconfig-f271638000f4f11a/out/config.rs +``` + +我们直接打开这个文件看即可: +```rust +// Platform constants and parameters for aarch64-qemu-virt. +// Generated by build.rs, DO NOT edit! + +/// Architecture identifier. +pub const ARCH: &str = "aarch64"; +/// Platform identifier. +pub const PLATFORM: &str = "aarch64-qemu-virt"; +/// Platform family. +pub const FAMILY: &str = "aarch64-qemu-virt"; +/// Base address of the whole physical memory. +pub const PHYS_MEMORY_BASE: usize = 0x4000_0000; +/// Size of the whole physical memory. +pub const PHYS_MEMORY_SIZE: usize = 0x800_0000; +/// Base physical address of the kernel image. +pub const KERNEL_BASE_PADDR: usize = 0x4008_0000; +/// Base virtual address of the kernel image. +pub const KERNEL_BASE_VADDR: usize = 0xffff_0000_4008_0000; +/// Linear mapping offset, for quick conversions between physical and virtual +/// addresses. +pub const PHYS_VIRT_OFFSET: usize = 0xffff_0000_0000_0000; +/// Offset of bus address and phys address. some boards, the bus address is +/// different from the physical address. +pub const PHYS_BUS_OFFSET: usize = 0; +/// Kernel address space base. +pub const KERNEL_ASPACE_BASE: usize = 0xffff_0000_0000_0000; +/// Kernel address space size. +pub const KERNEL_ASPACE_SIZE: usize = 0x0000_ffff_ffff_f000; +/// MMIO regions with format (`base_paddr`, `size`). +pub const MMIO_REGIONS: &[(usize, usize)] = &[ + (0x0900_0000, 0x1000), + (0x0910_0000, 0x1000), + (0x0800_0000, 0x2_0000), + (0x0a00_0000, 0x4000), + (0x1000_0000, 0x2eff_0000), + (0x40_1000_0000, 0x1000_0000), +]; +/// VirtIO MMIO regions with format (`base_paddr`, `size`). +pub const VIRTIO_MMIO_REGIONS: &[(usize, usize)] = &[ + (0x0a00_0000, 0x200), + (0x0a00_0200, 0x200), + (0x0a00_0400, 0x200), + (0x0a00_0600, 0x200), + (0x0a00_0800, 0x200), + (0x0a00_0a00, 0x200), + (0x0a00_0c00, 0x200), + (0x0a00_0e00, 0x200), + (0x0a00_1000, 0x200), + (0x0a00_1200, 0x200), + (0x0a00_1400, 0x200), + (0x0a00_1600, 0x200), + (0x0a00_1800, 0x200), + (0x0a00_1a00, 0x200), + (0x0a00_1c00, 0x200), + (0x0a00_1e00, 0x200), + (0x0a00_3000, 0x200), + (0x0a00_2200, 0x200), + (0x0a00_2400, 0x200), + (0x0a00_2600, 0x200), + (0x0a00_2800, 0x200), + (0x0a00_2a00, 0x200), + (0x0a00_2c00, 0x200), + (0x0a00_2e00, 0x200), + (0x0a00_3000, 0x200), + (0x0a00_3200, 0x200), + (0x0a00_3400, 0x200), + (0x0a00_3600, 0x200), + (0x0a00_3800, 0x200), + (0x0a00_3a00, 0x200), + (0x0a00_3c00, 0x200), + (0x0a00_3e00, 0x200), +]; +/// Base physical address of the PCIe ECAM space. +pub const PCI_ECAM_BASE: usize = 0x40_1000_0000; +/// End PCI bus number (`bus-range` property in device tree). +pub const PCI_BUS_END: usize = 0xff; +/// PCI device memory ranges (`ranges` property in device tree). +pub const PCI_RANGES: &[(usize, usize)] = &[ + (0x3ef_f0000, 0x1_0000), + (0x1000_0000, 0x2eff_0000), + (0x80_0000_0000, 0x80_0000_0000), +]; +/// UART Address +pub const UART_PADDR: usize = 0x0900_0000; + +pub const UART_IRQ: usize = 1; +/// GICC Address +pub const GICC_PADDR: usize = 0x0801_0000; + +pub const GICD_PADDR: usize = 0x0800_0000; +/// PSCI +pub const PSCI_METHOD: &str = "hvc"; +/// pl031@9010000 { +/// clock-names = "apb_pclk"; +/// clocks = <0x8000>; +/// interrupts = <0x00 0x02 0x04>; +/// reg = <0x00 0x9010000 0x00 0x1000>; +/// compatible = "arm,pl031\0arm,primecell"; +/// }; +/// RTC (PL031) Address +pub const RTC_PADDR: usize = 0x901_0000; +/// Timer interrupt frequency in Hz. +pub const TIMER_FREQUENCY: usize = 0; +/// Stack size of each task. +pub const TASK_STACK_SIZE: usize = 0x40000; +/// Number of timer ticks per second (Hz). A timer tick may contain several timer +/// interrupts. +pub const TICKS_PER_SEC: usize = 100; +/// Number of CPUs +pub const SMP: usize = 1; + +``` + +由于`modules/axconfig/src/lib.rs`里有: +```rust +#[rustfmt::skip] +mod config { + include!(concat!(env!("OUT_DIR"), "/config.rs")); +} +``` + +那么我们可以看到一些使用了`axconfig::xx`的引用的来源. + +## 透传到`host`的`net0`网络后端和`5555`端口是怎么被访问到的? + + +![](00%20inbox/asset/Pasted%20image%2020241208180438.png) + +### 解析`modules/axdriver/src/ixgbe.rs` + + + + +## log + +这里是log: +```shell +qemu-system-aarch64 -m 128M -smp 2 -cpu cortex-a72 -machine virt -kernel examples/httpclient/httpclient_aarch64-qemu-virt.bin -device virtio-net-pci,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:5555,hostfwd=udp::5555-:5555 -nographic + + 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 = aarch64 +platform = aarch64-qemu-virt +target = aarch64-unknown-none-softfloat +smp = 2 +build_mode = release +log_level = debug + +[ 0.005105 axruntime:130] Logging is enabled. +[ 0.006216 axruntime:131] Primary CPU 0 started, dtb = 0x44000000. +[ 0.007098 axruntime:133] Found physcial memory regions: +[ 0.007682 axruntime:135] [PA:0x40080000, PA:0x400a6000) .text (READ | EXECUTE | RESERVED) +[ 0.008824 axruntime:135] [PA:0x400a6000, PA:0x400af000) .rodata (READ | RESERVED) +[ 0.009895 axruntime:135] [PA:0x400af000, PA:0x400b3000) .data .tdata .tbss .percpu (READ | WRITE | RESERVED) +[ 0.011083 axruntime:135] [PA:0x400b3000, PA:0x40133000) boot stack (READ | WRITE | RESERVED) +[ 0.011895 axruntime:135] [PA:0x40133000, PA:0x40159000) .bss (READ | WRITE | RESERVED) +[ 0.012720 axruntime:135] [PA:0x40159000, PA:0x48000000) free memory (READ | WRITE | FREE) +[ 0.013704 axruntime:135] [PA:0x9000000, PA:0x9001000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 0.014160 axruntime:135] [PA:0x9100000, PA:0x9101000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 0.015061 axruntime:135] [PA:0x8000000, PA:0x8020000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 0.016082 axruntime:135] [PA:0xa000000, PA:0xa004000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 0.017048 axruntime:135] [PA:0x10000000, PA:0x3eff0000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 0.017742 axruntime:135] [PA:0x4010000000, PA:0x4020000000) mmio (READ | WRITE | DEVICE | RESERVED) +[ 0.018484 axruntime:208] Initialize global memory allocator... +[ 0.019127 axruntime:209] use TLSF allocator. +[ 0.019775 axalloc:212] initialize global allocator at: [0xffff000040159000, 0xffff000048000000) +[ 0.020798 axmm:60] Initialize virtual memory management... +[ 0.027013 axmm:63] kernel address space init OK: AddrSpace { + va_range: VA:0xffff000000000000..VA:0xfffffffffffff000, + page_table_root: PA:0x40161000, +} +[ 0.028517 axruntime:150] Initialize platform devices... +[ 0.028923 axdriver:152] Initialize device drivers... +[ 0.029312 axdriver:153] device model: static +[ 0.029799 axdriver::bus::pci:97] PCI 00:00.0: 1b36:0008 (class 06.00, rev 00) Standard +[ 0.031156 axdriver::bus::pci:97] PCI 00:01.0: 1af4:1000 (class 02.00, rev 00) Standard +[ 0.031683 axdriver::bus::pci:54] BAR 1: MEM [0x10000000, 0x10001000) +[ 0.032182 axdriver::bus::pci:54] BAR 4: MEM [0x10004000, 0x10008000) 64bit pref +[ 0.034618 virtio_drivers::transport:78] Device features: Features(CTRL_GUEST_OFFLOADS | MAC | MRG_RXBUF | STATUS | CTRL_VQ | CTRL_RX | CTRL_VLAN | CTRL_RX_EXTRA | GUEST_ANNOUNCE | CTL_MAC_ADDR | RING_INDIRECT_DESC | RING_EVENT_IDX | VERSION_1) +[ 0.036068 virtio_drivers::device::net::dev_raw:30] negotiated_features Features(MAC | STATUS | RING_INDIRECT_DESC | RING_EVENT_IDX) +[ 0.036781 virtio_drivers::device::net::dev_raw:37] Got MAC=[52, 54, 00, 12, 34, 56], status=Status(LINK_UP) +[ 0.038039 axalloc:118] expand heap memory: [0xffff0000403a5000, 0xffff0000403e5000) +[ 0.039351 axdriver::bus::pci:104] registered a new Net device at 00:01.0: "virtio-net" +[ 0.050505 axdriver:160] number of NICs: 1 +[ 0.050868 axdriver:163] NIC 0: "virtio-net" +[ 0.051241 axnet:43] Initialize network subsystem... +[ 0.051457 axnet:46] use NIC 0: "virtio-net" +[ 0.051956 axalloc:118] expand heap memory: [0xffff0000403e5000, 0xffff0000404e5000) +[ 0.052391 axalloc:118] expand heap memory: [0xffff0000404e5000, 0xffff0000406e5000) +[ 0.053174 axnet::smoltcp_impl:332] created net interface "eth0": +[ 0.053471 axnet::smoltcp_impl:333] ether: 52-54-00-12-34-56 +[ 0.054021 axnet::smoltcp_impl:334] ip: 10.0.2.15/24 +[ 0.054368 axnet::smoltcp_impl:335] gateway: 10.0.2.2 +[ 0.054664 axruntime::mp:19] starting CPU 1... +[ 0.054937 axhal::platform::aarch64_common::psci:113] Starting CPU 1 ON ... +[ 0.055480 axruntime:186] Primary CPU 0 init OK. +[ 0.056642 axruntime::mp:36] Secondary CPU 1 started. +[ 0.056872 axruntime::mp:46] Secondary CPU 1 init OK. +Hello, simple http client! +dest: 49.12.234.183:80 (49.12.234.183:80) +[ 0.058237 0 axnet::smoltcp_impl:100] socket #0: created +[ 0.059213 0 smoltcp::iface::interface:1473] address 10.0.2.2 not in neighbor cache, sending ARP request +[ 0.386031 0 axnet::smoltcp_impl::tcp:430] TCP socket #0: connected to 49.12.234.183:80 +HTTP/1.1 200 OK +Server: nginx +Date: Sat, 07 Dec 2024 07:03:37 GMT +Content-Type: text/plain +Content-Length: 14 +Connection: keep-alive +Access-Control-Allow-Origin: * +Cache-Control: no-cache, no-store, must-revalidate + +211.83.106.222 +[ 0.675625 0 axnet::smoltcp_impl::tcp:247] TCP socket #0: shutting down +[ 0.676139 0 axnet::smoltcp_impl:128] socket #0: destroyed +[ 0.676366 0 axruntime:199] main task exited: exit_code=0 +[ 0.676586 0 axhal::platform::aarch64_common::psci:96] Shutting down... +``` + +# 技术交流会议内容 + +## 技术文档 + +[igb-driver/doc/tack.md at main · qclic/igb-driver](https://github.com/qclic/igb-driver/blob/main/doc/tack.md) + +## 会议内容 + +主要看一下设备树和BAR空间的概念。 + +4.5节讲了怎么进行初始化。 + +![](00%20inbox/asset/Pasted%20image%2020241210234315.png) + + +怎么完成这个初始化呢?**操作寄存器** + +![](00%20inbox/asset/Pasted%20image%2020241210234457.png) + +说明reset成功 +![](00%20inbox/asset/Pasted%20image%2020241210235447.png) + +![](00%20inbox/asset/Pasted%20image%2020241210235417.png) + +![](00%20inbox/asset/Pasted%20image%2020241210235349.png) + + +# 初始化流程 + +## 关中断 + +![](00%20inbox/asset/Pasted%20image%2020241211185911.png) + +### 在发出全局重置后,也需要禁用中断 + +- [ ] + +### IMC 寄存器 + +![](00%20inbox/asset/Pasted%20image%2020241211190003.png) +![](00%20inbox/asset/Pasted%20image%2020241211191138.png) + +## 全局Reset并且进行常规配置 + +![](00%20inbox/asset/Pasted%20image%2020241211192440.png) + +### `CTRL`寄存器 + +![](00%20inbox/asset/Pasted%20image%2020241211193835.png) + +### `RCTL` 寄存器 + +![](00%20inbox/asset/Pasted%20image%2020241211204307.png) + +### `TCTL`寄存器 + +![](00%20inbox/asset/Pasted%20image%2020241211204333.png) + +### `RXPBS`寄存器 + +![](00%20inbox/asset/Pasted%20image%2020241211204419.png) + +### `TXPBS`寄存器 + +![](00%20inbox/asset/Pasted%20image%2020241211204432.png) + +## 设置PHY和链接 + +### CTRL_EXT + +![](00%20inbox/asset/Pasted%20image%2020241211232342.png) + +### Copper PHY Link设置 + +[以太网——PHY、MAC和 MII基础知识 - 知乎](https://zhuanlan.zhihu.com/p/585923184) + +![](00%20inbox/asset/Pasted%20image%2020241211233057.png) + +#### 自动协商 + +链路的解析流控制行为要放到`CTRL.TFCE`和`CTRL.RFCE`里. + +在自动协商结束后,MAC从PHY中识别"链接指示"之前需要设置`CTRL.SLU`. + +#### MAC速度解决 + +1. 根据PHY指示的速度来进行解决 + 1. 直接读取PHY寄存器,详见下面一节MDIO的读取 + 2. 通过STATUS.SPEED 寄存器来读取PHY的SPD_IND寄存器 +2. 软件要求MAC尝试从PHY到MAC RX_CLK自动检测PHY速度,然后相应地编程MAC速度 +3. MAC通过使用PHY的内部PHY-到MAC速度指示(SPD_IND),基于PHY指示自动检测和设置MAC的链路速度 + +##### 强制设置MAC速度 + +设置`CTRL.frcspd`和`CTRL.speed` + +> 这里有一点非常重要,就是在设置一个位的时候,假如我们不知道其原始状态,我们应该先将其清零,然后再设置 + +##### 使用PHY的指示值 + +设置`CTRL.ASDE`和`CTRL.FRCSPD`值 + +#### MAC双工解决 + +设置`CTRL.frcdplx`和`CTRL.FD` + +#### 使用PHY寄存器 + +暂且跳过,这部分是高级功能,直接通过MDIO接口操作PHY设备 + +### MDIO接口 + +1. 物理接口 +2. 特殊协议-可以跨连接运行 +3. 一组内部的可寻址寄存器 + +内部和外扩的接口由: +1. 数据线MDIO +2. 时钟线MDC + +通过访问MAC的寄存器可以访问这两种接口. + +MDC是MDIO的时钟线,这个时钟信号不一定一直要有,只要在有数据的时候有没数据的时候就冻结就行了.最大工作频率为2.5MHz. + +MDIO是一种传送PHY和MAC之间命令的双向数据信号,因此MAC可以通过一些指令来读取和写PHY管理寄存器. + +直接通过访问MDIC寄存器即可访问这个接口. + +### MDIC寄存器 + +![](00%20inbox/asset/Pasted%20image%2020241214165528.png) + +> The PHY address for MDIO accesses is 00001b. + +![](00%20inbox/asset/Pasted%20image%2020241215023254.png) + +## MAC/PHY 链接设置 + +这个有4种方法,按照情况 + +### 自动设置 + +只需要设置`CTRL.FRCDPLX`和`CTRL.FRCSPD`为`0b`. + +### 启动PHY的自动协商 + +![](00%20inbox/asset/Pasted%20image%2020241217194356.png) + +## 接收寄存器初始化 + +![](00%20inbox/asset/Pasted%20image%2020241217194257.png) + +### 初始化多播表 + +![](00%20inbox/asset/Pasted%20image%2020241217194316.png) +![](00%20inbox/asset/Pasted%20image%2020241217194445.png) + +![](00%20inbox/asset/Pasted%20image%2020241217194530.png) + +![](00%20inbox/asset/Pasted%20image%2020241217194634.png) + +然后这个是11位也就是`[11:0]`, + +那么`[11:5]`决定指向哪个寄存器,刚好128个寄存器嘛. + +那么`[4:0]`决定是指向哪个位,刚好32个位嘛. + +## 接收功能 + +主要是要学一下`rings`. + +### Rx队列分配 + +接收到的包分为三个阶段: +1. L2 Filters 用于确保包已经收到 +2. Pool Select 用于虚拟化环境,定义Rx包的目标虚拟端口(称之为**池**)一个数据包可以与任意数量的端口/池相关联 +3. Queue Select 这一步Rx包已经成功通过过滤器,并且和一个或者多个**接收描述符队列**相连接 + +![](00%20inbox/asset/Pasted%20image%2020241217203351.png) + +这是一个类开关结构. + +> 这是一种**网络资源虚拟化**,把网卡的接口也就是最上边的,虚拟化成很多有**粒度**的资源 + +#### 接收数据包的目的地 + +- 虚拟化 +- RSS +- L2以太网过滤器 +- L3/L4 5-元组过滤器 +- TCP SYN筛选器 + +>通常,包接收包括识别线上包的存在、执行地址过滤、将包存储在接收数据FIFO中、将数据传输到主机存储器中的16个接收队列中的一个,以及更新接收描述符的状态。 + +![](00%20inbox/asset/Pasted%20image%2020241218010623.png) + +队列分配. + +![](00%20inbox/asset/Pasted%20image%2020241218010805.png) + +##### 虚拟化 + +在虚拟化环境中,**DMA资源**被多个软件**共享**. + +通过在DMA中分配 **接收描述符队列(receive descriptor queues)** 来完成虚拟化. + +将**队列**分配到**虚拟分区**是按**集合**完成的,每个**集合**都有相同数量的**队列**,那么这个队列组合叫做**池**. + +虚拟化会为**每个接收到的数据包**分配**一个或者多个池索引**. + +包的分配是根据**池索引**和一些其它的**约束**来分配到池中的. + +##### RSS + +RSS通过将**数据包**分配到不同的**描述符队列**中,在多个处理器核心之间分配数据包处理. + +RSS为每个接收到的数据包分配一个**RSS索引**. + +包的分配是根据**RSS索引**和一些其它的**约束**(比如上述提到的池索引)来分配到池中的. + +##### L2以太网过滤器 + +这些过滤器根据其**L2以太网类型**来**识别**数据包,并将它们分配给接收队列. + +##### L3/L4 5-元组过滤器 + +识别出**指定的**L3/L4流,或者L3/L4流的组合. + +每个过滤器由一个5个元组组成(**协议、源和目标IP地址、源和目标TCP/ UDP端口**). + +##### TCP SYN筛选器 + +把有`SYN`标志的`TCP`包专门放到一个**独立的队列**里监视. + +### 非虚拟化中的队列 + +![](00%20inbox/asset/Pasted%20image%2020241218012449.png) +### 队列设置寄存器 + +每一个队列会有一套配置寄存器,用于控制队列操作。 + +- RDBAL 和 RDBAH — 接收描述符基址 +- RDLEN — 接收描述符长度 +- RDH — 接收描述符头 +- RDT — 接收描述符尾 +- RXDCTL — 接收描述符控制 +- RXCTL — 接收 DCA 控制 + +>DCA在网络硬件接口和网络驱动中通常指的是“Direct Cache Access”,即直接缓存访问。DCA是一种硬件技术,旨在通过减少数据在内存和处理器之间的传输延迟来提高网络数据包处理的效率。具体来说,DCA可以将网络数据直接写入处理器的缓存,从而提高数据处理的速度和效率。 + +一共有16个队列. + +定义描述符队列功能的**CSR**被复制了8个副本给**虚拟功能(VF,Virtual Function)索引**,以实现虚拟化. + +每一套被复制的寄存器对应了**一组**队列,这些队列的VF index相同. + +> 注意是**CSR**被复制了8份,而不是队列被复制了8份. + +> 这个虚拟化不是按照队列来划分的,不是说一个队列虚拟出几个队列.而是每个功能都认为自己拥有整个网卡,所以假设你的网卡支持8个虚拟功能(VF),那么每个描述符队列的控制和状态寄存器会被复制8份,每份对应一个VF index(从0到7)。 +> (Gen by GPT-4o) + +>**SRRCTL(Split and Replication Receive Control)** 寄存器用于控制接收数据包的拆分和复制功能。这些功能在处理网络数据包时可以提高灵活性和性能。 +>主要功能包括: +>1. **数据包拆分(Split)**: + - 可以将接收到的网络数据包拆分成多个部分,以便于在不同的内存区域中存储。这样可以提高数据包处理的效率,特别是在数据包非常大的情况下。 + - 通过拆分数据包,能够更高效地利用缓存和内存,从而减少处理延迟。 +>2. **数据包复制(Replication)**: + - 允许将接收到的数据包复制到多个队列中。这样可以支持多播和广播数据包的高效处理。 + - 数据包复制有助于在多个处理器核心之间分配接收的网络流量,从而提高处理吞吐量。 +>SRRCTL寄存器中的各个字段可以配置这些功能的具体行为,例如启用或禁用拆分和复制、设置拆分的阈值等。 + +>**PSRTYPE(Packet Split Receive Type)** 寄存器用于指定数据包拆分的类型和模式。它定义了如何将接收到的数据包拆分成不同的部分。 +>主要功能包括: +>1. **拆分模式选择**: + - PSRTYPE寄存器允许选择不同的拆分模式,例如基于数据包头部的拆分、基于数据包长度的拆分等。 + - 不同的拆分模式适用于不同的应用场景,例如高效处理TCP/IP头部、优化内存使用等。 +>2. **拆分类型配置**: + - 可以配置拆分的具体类型,例如将数据包拆分成固定大小的块、将头部和数据部分分开存储等。 + - 通过配置拆分类型,可以更好地优化数据包的处理和存储,提高整体性能。 +>PSRTYPE寄存器中的字段用于详细配置这些拆分模式和类型,确保数据包能够按照预期方式进行拆分和处理。 + +有了上述功能,那么每个虚拟化功能(VF)就可以按照自己的喜好来处理每个数据包,就好像每个数据包都专门发给他一样,**实际上**是拆分复制处理过的数据包. + + +### 通过L2 Filter 来确保包已经收到 + +`L2 Filter`被翻译为**二级数据包过滤**. + +![](00%20inbox/asset/Pasted%20image%2020241217205544.png) + +##### MAC地址过滤 + +###### 单播过滤器 + +###### 多播过滤器 + +##### VLAN 过滤 + +![](00%20inbox/asset/Pasted%20image%2020241217210226.png) + +##### 可管理性过滤 + +![](00%20inbox/asset/Pasted%20image%2020241217210707.png) + +### 五元组过滤器 + +`#todo` + +### RSS接收侧缩放 + +RSS是一种将接收到的数据包分发到多个描述符队列中的机制。然后,软件将每个队列分配给不同的处理器,在多个处理器之间共享数据包处理的负载。 + +### VLAN过滤器 + +`#todo` + +## 接收数据存储 + +### Host Buffer + +其大小可以通过`RCTL.BSIZE`设置`packat`的`buffer`的大小,会影响所有的接收队列. + +也可也通过**每个队列**的`SRRCTL[n].BSIZEPACKET`来设置.在设置为`0`的时候是采用`RCTL.BSIZE` 的默认设置. + +在使用`advanced descriptor`的时候需要通过设置`SRRCTL.BSIZEHEADER`来决定`header`的`buffer`的大小. + +### 片上Rx Buffer + +>82576包含一个64 KBytes的包缓冲区,可用于存储包,直到它们被转发到主机。 +>此外,为了支持第7.10.3节所述的本地包的转发,提供了20kbyt的交换机缓冲区。此缓冲区作为所有本地流量的接收缓冲区。 + +### 片上descriptor Buffer + +> 82576为每个接收队列包含一个32个描述符缓存,用于减少包处理的延迟,并通过突发中获取和写回描述符来优化PCIe带宽的使用。 + +## 传统接收描述符格式 + +在`SRRCTL[n],DESCTYPE = 000b` + +![](00%20inbox/asset/Pasted%20image%2020241220004631.png) + +> 这里ixgbe暂时跳过了,我们也暂时跳过,可以根据Advanced Descriptor来进行后续的补充工作 + +## 接收描述符Ring结构 + + + + +# 移植到ixgbe代码 + +## 寻找igb的id + +我们用的是82576,在`ethernet-linux-igb`,也就是`linux`的`igb`驱动项目里的`src/e1000_hw.h`文件夹里可以查出. + +> E1000_DEV_ID_82576          0x10C9 + +## 解决宏定义问题 + +在`ixgbe-driver`的`src/constants.rs`文件里存在大量的宏,是用于规定一些需要的. + +我们把`ethernet-linux-igb`中的`src/e1000_hw.h`的`E1000_DEV_ID_82576`等宏改为`rust`形式,并且重新命名. + +> 例如把`E1000_DEV_ID_82576`的名字改为`IGB_DEV_ID_82576`. + +## 读取MAC + +![](00%20inbox/asset/Pasted%20image%2020241216194732.png) + +![](00%20inbox/asset/Pasted%20image%2020241216194803.png) + +> 这里有一点语焉不详的地方,就是`RAL0`和`RAH0`是从`EEPROM`加载出来的数据,那么这里有我们需要的数据. + + +# TIPS + +## git的换行符问题 + +有时候是从windows clone下来的,所以在wsl里是linux方式读取,因此刚刚`clone`下来就是"被更改的状态". + +```bash +git config --global core.autocrlf true +``` + +# 下一步 + +- [ ] 尝试直接把`axdriver_net`给pull下来,然后把它加一个本地地址,然后把他的依赖改成`workspace`. +[KuangjuX/ixgbe-driver: Intel 82599+ 10Gb NIC Driver.](https://github.com/KuangjuX/ixgbe-driver) +- [ ] `interrupts`和`descriptor`的代码没移植,要考虑在手册里理解这个概念之后在做 +- [ ] 寻找`ixgbe`这个框架里和初始化相关的部分,先把会写的部分移植上 +[Linux系统e1000e网络驱动源码(最深入)分析过程-CSDN博客](https://blog.csdn.net/Luckiers/article/details/123645030) +[[MIT 6.S081] Lab 11: networking_c版本的e1000网卡实现-CSDN博客](https://blog.csdn.net/LostUnravel/article/details/121437373) + 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`. + + +