diff --git a/docs/general/help.md b/docs/general/help.md index cf95372..3d5ceda 100644 --- a/docs/general/help.md +++ b/docs/general/help.md @@ -58,18 +58,18 @@ no_comments: true 提问时需要避免 X-Y 问题:即你实际遇到了 X 问题,你认为 X 问题要用 Y 方法解决,在实现 Y 方法时遇到困难,然后向他人提问如何实现 Y 方法。但很多时候 Y 方法并不是 X 问题的正确解法,此时可能会得到错误的答案。引用 [X-Y Problem](https://xyproblem.info/) 的一个例子: -- X 问题:想要截取文件名的后缀名 -- Y 方法:截取文件名的后三位字符 -- 此时的 Y 方法并不是解决 X 问题的正确方法,因为后缀名不一定是三个字符 -- 正确的 X 问题的解法是找到最后一个 `.`,然后提取它之后的字符 -- 如果提问的时候只提及了 Y 方法,而没有 X 问题,就会出现这样的问题 +- X 问题:想要截取文件名的后缀名 +- Y 方法:截取文件名的后三位字符 +- 此时的 Y 方法并不是解决 X 问题的正确方法,因为后缀名不一定是三个字符 +- 正确的 X 问题的解法是找到最后一个 `.`,然后提取它之后的字符 +- 如果提问的时候只提及了 Y 方法,而没有 X 问题,就会出现这样的问题 因此在提问时,为了避免 X-Y 问题,建议按照下面的模板来描述完整的问题: -- 我正在做 X 问题 -- 我认为为了解决 X 问题,可以用 Y 方法 -- 在解决 X 问题时,采用了 Y 方法,过程中遇到了 Z 问题,尝试用 A 方法解决,但是没有效果 +- 我正在做 X 问题 +- 我认为为了解决 X 问题,可以用 Y 方法 +- 在解决 X 问题时,采用了 Y 方法,过程中遇到了 Z 问题,尝试用 A 方法解决,但是没有效果 而不是: -- 我遇到了 Z 问题,求救 T_T +- 我遇到了 Z 问题,求救 T_T diff --git a/docs/general/specification.md b/docs/general/specification.md index 911c7ca..77ae3bc 100644 --- a/docs/general/specification.md +++ b/docs/general/specification.md @@ -46,9 +46,9 @@ no_comments: true 建议使用工具来辅助代码风格的检查: -- 使用 `rustfmt`,`cargo fmt --all` 命令来格式化代码; -- 使用 `clippy`,`cargo clippy` 来检查代码风格。 -- 使用 [typos](https://github.com/crate-ci/typos) 检查拼写错误,可以使用 `cargo install typos-cli` 安装。 +- 使用 `rustfmt`,`cargo fmt --all` 命令来格式化代码; +- 使用 `clippy`,`cargo clippy` 来检查代码风格。 +- 使用 [typos](https://github.com/crate-ci/typos) 检查拼写错误,可以使用 `cargo install typos-cli` 安装。 !!! note "请注意,由于项目 target 不尽相同,`clippy` 需要在每一个 `package` 下使用。" @@ -56,18 +56,18 @@ no_comments: true ### 提交历史 -- 每个提交都应该有一定的意义,例如实现了新功能,修复了一个问题,定义了新的函数; -- 比较复杂的程序,要边开发边提交,而不是写完了再一次性提交; -- 不强求线性历史,**但是不允许使用 force push**。 +- 每个提交都应该有一定的意义,例如实现了新功能,修复了一个问题,定义了新的函数; +- 比较复杂的程序,要边开发边提交,而不是写完了再一次性提交; +- 不强求线性历史,**但是不允许使用 force push**。 ### 提交消息 -- 简单明了地描述这个提交的内容; -- 建议用英文写,用中文写也可以; -- 不要编写的过于详细或过于简略; -- 可以采用一些格式,例如 [**Conventional Commits**](https://www.conventionalcommits.org/en/v1.0.0/#examples); -- 不掺杂个人情绪; -- 可以添加一些 Emoji,[gitmoji](https://gitmoji.dev/) 为提交说明中使用的 Emoji 提出了一些建议,可以参考。 +- 简单明了地描述这个提交的内容; +- 建议用英文写,用中文写也可以; +- 不要编写的过于详细或过于简略; +- 可以采用一些格式,例如 [**Conventional Commits**](https://www.conventionalcommits.org/en/v1.0.0/#examples); +- 不掺杂个人情绪; +- 可以添加一些 Emoji,[gitmoji](https://gitmoji.dev/) 为提交说明中使用的 Emoji 提出了一些建议,可以参考。 ### 代码打包 diff --git a/docs/index.md b/docs/index.md index 89d155a..9b55bd9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,12 +10,12 @@ 每一次实验区间为两周。 -- [实验零:环境搭建与实验准备](./labs/0x00/index.md) -- [实验一:操作系统的启动](./labs/0x01/index.md) -- [实验二:中断处理](./labs/0x02/index.md) -- [实验三:内核线程与缺页异常](./labs/0x03/index.md) -- [实验四:用户程序与系统调用](./labs/0x04/index.md) -- [实验五:fork 的实现、并发与锁机制](./labs/0x05/index.md) -- [实验六:硬盘驱动与文件系统](./labs/0x06/index.md) -- [实验七:更好的内存管理](./labs/0x07/index.md) -- [实验八:扩展实验](./labs/0x08/index.md) +- [实验零:环境搭建与实验准备](./labs/0x00/index.md) +- [实验一:操作系统的启动](./labs/0x01/index.md) +- [实验二:中断处理](./labs/0x02/index.md) +- [实验三:内核线程与缺页异常](./labs/0x03/index.md) +- [实验四:用户程序与系统调用](./labs/0x04/index.md) +- [实验五:fork 的实现、并发与锁机制](./labs/0x05/index.md) +- [实验六:硬盘驱动与文件系统](./labs/0x06/index.md) +- [实验七:更好的内存管理](./labs/0x07/index.md) +- [实验八:扩展实验](./labs/0x08/index.md) diff --git a/docs/labs/0x00/index.md b/docs/labs/0x00/index.md index 47c92ce..5bddf25 100644 --- a/docs/labs/0x00/index.md +++ b/docs/labs/0x00/index.md @@ -22,11 +22,11 @@ 对于本次实验内容,你需要参考学习如下实验资料: -- [Linux 环境配置](../../wiki/linux.md) -- [Windows 环境配置](../../wiki/windows.md) -- [Rust 语言学习](../../wiki/rust.md) -- [UEFI 启动过程](../../wiki/uefi.md) -- [QEMU 使用参考](../../wiki/qemu.md) +- [Linux 环境配置](../../wiki/linux.md) +- [Windows 环境配置](../../wiki/windows.md) +- [Rust 语言学习](../../wiki/rust.md) +- [UEFI 启动过程](../../wiki/uefi.md) +- [QEMU 使用参考](../../wiki/qemu.md) ## 实验任务与要求 diff --git a/docs/labs/0x00/tasks.md b/docs/labs/0x00/tasks.md index 39c20e2..f6f3856 100644 --- a/docs/labs/0x00/tasks.md +++ b/docs/labs/0x00/tasks.md @@ -14,10 +14,10 @@ 我们推荐在以下环境进行实验: -- Ubuntu 22.04 LTS (jammy) on WSL 2 **(推荐 Windows 用户选择)** -- Windows 10/11 **(Windows 原生备选,GDB 相关功能无法使用)** -- Ubuntu 22.04 LTS (jammy) -- macOS with Apple Silicon +- Ubuntu 22.04 LTS (jammy) on WSL 2 **(推荐 Windows 用户选择)** +- Windows 10/11 **(Windows 原生备选,GDB 相关功能无法使用)** +- Ubuntu 22.04 LTS (jammy) +- macOS with Apple Silicon 以上环境经过我们的测试和验证,可以正常进行实验。对于其他常用的 Linux 发行版,通常也可以正常进行实验,但我们不提供技术支持。 @@ -33,11 +33,11 @@ 在 Windows 平台上我们建议通过 VSCode + Python + CodeLLDB 插件进行开发、调试。 -- 对于选择使用 Linux 的同学,请参考 [Linux 环境配置](../../wiki/linux.md) 进行配置,**文档包含 Linux 相关安装指南**。 +- 对于选择使用 Linux 的同学,请参考 [Linux 环境配置](../../wiki/linux.md) 进行配置,**文档包含 Linux 相关安装指南**。 -- 对于选择使用 Windows 的同学,请参考 [Windows 环境配置](../../wiki/windows.md) 进行配置。 +- 对于选择使用 Windows 的同学,请参考 [Windows 环境配置](../../wiki/windows.md) 进行配置。 -- 对于选择使用 macOS 的同学,请安装 `brew` 和相应工具,参考 [Linux 环境配置](../../wiki/linux.md) 进行配置。 +- 对于选择使用 macOS 的同学,请安装 `brew` 和相应工具,参考 [Linux 环境配置](../../wiki/linux.md) 进行配置。 ## 尝试使用 Rust 进行编程 @@ -129,10 +129,10 @@ 尝试输出如下格式和内容: - - `INFO: Hello, world!`,其中 `INFO:` 为绿色,后续内容为白色 - - `WARNING: I'm a teapot!`,颜色为黄色,加粗,并为 `WARNING` 添加下划线 - - `ERROR: KERNEL PANIC!!!`,颜色为红色,加粗,并尝试让这一行在控制行窗口居中 - - 一些你想尝试的其他效果和内容…… + - `INFO: Hello, world!`,其中 `INFO:` 为绿色,后续内容为白色 + - `WARNING: I'm a teapot!`,颜色为黄色,加粗,并为 `WARNING` 添加下划线 + - `ERROR: KERNEL PANIC!!!`,颜色为红色,加粗,并尝试让这一行在控制行窗口居中 + - 一些你想尝试的其他效果和内容…… !!! tip "如果你想进一步了解,可以尝试搜索 **ANSI 转义序列**" @@ -140,9 +140,9 @@ 实现一个名为 `Shape` 的枚举,并为它实现 `pub fn area(&self) -> f64` 方法,用于计算不同形状的面积。 - - 你可能需要使用模式匹配来达到相应的功能 - - 请实现 `Rectangle` 和 `Circle` 两种 `Shape`,并使得 `area` 函数能够正确计算它们的面积 - - 使得你的实现能够通过如下测试: + - 你可能需要使用模式匹配来达到相应的功能 + - 请实现 `Rectangle` 和 `Circle` 两种 `Shape`,并使得 `area` 函数能够正确计算它们的面积 + - 使得你的实现能够通过如下测试: ```rust #[test] @@ -164,9 +164,9 @@ 使得每次调用 `UniqueId::new()` 时总会得到一个新的不重复的 `UniqueId`。 - - 你可以在函数体中定义 `static` 变量来存储一些全局状态 - - 你可以尝试使用 `std::sync::atomic::AtomicU16` 来确保多线程下的正确性(无需进行验证,相关原理将在 Lab 5 介绍,此处不做要求) - - 使得你的实现能够通过如下测试: + - 你可以在函数体中定义 `static` 变量来存储一些全局状态 + - 你可以尝试使用 `std::sync::atomic::AtomicU16` 来确保多线程下的正确性(无需进行验证,相关原理将在 Lab 5 介绍,此处不做要求) + - 使得你的实现能够通过如下测试: ```rust #[test] diff --git a/docs/labs/0x01/index.md b/docs/labs/0x01/index.md index 146b247..8d19b46 100644 --- a/docs/labs/0x01/index.md +++ b/docs/labs/0x01/index.md @@ -21,10 +21,10 @@ 对于本次实验内容,你需要参考学习如下实验资料: -- [分页内存简述](../../wiki/paging.md) -- [ELF 文件格式](../../wiki/elf.md) -- [串口输出简介](../../wiki/uart.md) -- [实验调试指南](../../wiki/debug.md) +- [分页内存简述](../../wiki/paging.md) +- [ELF 文件格式](../../wiki/elf.md) +- [串口输出简介](../../wiki/uart.md) +- [实验调试指南](../../wiki/debug.md) ## 实验任务与要求 diff --git a/docs/labs/0x01/tasks.md b/docs/labs/0x01/tasks.md index 37c4baa..a736c5d 100644 --- a/docs/labs/0x01/tasks.md +++ b/docs/labs/0x01/tasks.md @@ -28,6 +28,8 @@ 请注意本次实验中的 `Makefile` 和 `ysos.py` 均有更新,并注意保留 `assets/OVMF.fd` 文件。 + 同时,自本次实验开始,采用工作区依赖的方式进行依赖管理,在根目录下的 `Cargo.toml` 中定义整个项目所需的依赖,并在 `pkg` 目录下的 `Cargo.toml` 中使用 `workspace = true` 的方式引用他们(详见参考代码)。 + 在 `pkg/kernel/config` 中,引用了 `config/x86_64-unknown-none.json` 的编译目标配置,该配置文件如下所示: ```json @@ -89,11 +91,11 @@ SECTIONS { 实验在 `pkg/boot` 中提供了一些基本的功能实现: -- `allocator.rs`:为 `uefi` crate 中的 `UEFIFrameAllocator` 实现 `x86_64` crate 所定义的 `FrameAllocator` trait,以便在页面分配、页表映射时使用。 -- `config.rs`:提供了一个读取并解析 `boot.conf` 的基本实现,可以使用它来自定义 bootloader 的行为、启动参数等等。 -- `fs.rs`:提供了在 UEFI 环境下打开文件、列出目录、加载文件、释放 `ElfFile` 的功能,你可以参考这部分代码了解与文件系统相关操作的基本内容。在后期的实验中,你将自己实现对文件系统的相关操作。 -- `lib.rs`:这部分内容定义了 bootloader 将要传递给内核的信息、内核的入口点、跳转到内核的实现等等。定义在 `lib.rs` 中是为了能够在内核实现中引用这些数据结构,确保内核与 bootloader 的数据结构一致。 -- `main.rs`:这里是 bootloader 的入口点,你可以在这里编写你的 bootloader 代码。 +- `allocator.rs`:为 `uefi` crate 中的 `UEFIFrameAllocator` 实现 `x86_64` crate 所定义的 `FrameAllocator` trait,以便在页面分配、页表映射时使用。 +- `config.rs`:提供了一个读取并解析 `boot.conf` 的基本实现,可以使用它来自定义 bootloader 的行为、启动参数等等。 +- `fs.rs`:提供了在 UEFI 环境下打开文件、列出目录、加载文件、释放 `ElfFile` 的功能,你可以参考这部分代码了解与文件系统相关操作的基本内容。在后期的实验中,你将自己实现对文件系统的相关操作。 +- `lib.rs`:这部分内容定义了 bootloader 将要传递给内核的信息、内核的入口点、跳转到内核的实现等等。定义在 `lib.rs` 中是为了能够在内核实现中引用这些数据结构,确保内核与 bootloader 的数据结构一致。 +- `main.rs`:这里是 bootloader 的入口点,你可以在这里编写你的 bootloader 代码。 同时在 `pkg/elf` 中实验提供了加载 ELF 文件的相关代码,其中也有需要你自己实现的部分。 @@ -178,9 +180,9 @@ unsafe { 最后,你需要检验是否成功加载了内核: -- 使用 `make build DBG_INFO=true` 或 `python ysos.py build -p debug` 编译内核,确保编译时开启了调试信息。 -- 使用 `make debug` 或 `python ysos.py launch -d` 启动 QEMU 并进入调试模式,这时候 QEMU 将会等待 GDB 的连接。 -- 在另一个终端中,使用 `gdb -q` 命令进入 GDB 调试环境。 +- 使用 `make build DBG_INFO=true` 或 `python ysos.py build -p debug` 编译内核,确保编译时开启了调试信息。 +- 使用 `make debug` 或 `python ysos.py launch -d` 启动 QEMU 并进入调试模式,这时候 QEMU 将会等待 GDB 的连接。 +- 在另一个终端中,使用 `gdb -q` 命令进入 GDB 调试环境。 !!! note "使用 `.gdbinit` 方便你的调试过程" @@ -193,8 +195,8 @@ unsafe { b ysos_kernel::init ``` -- 使用 `c` 命令继续执行,你将会看到 QEMU 窗口中的输出,同时 GDB 将会在断点处停下。 -- 查看断点处的汇编和符号是否正确,使用 `vmmap` 和 `readelf` 等指令查看内核的加载情况。 +- 使用 `c` 命令继续执行,你将会看到 QEMU 窗口中的输出,同时 GDB 将会在断点处停下。 +- 查看断点处的汇编和符号是否正确,使用 `vmmap` 和 `readelf` 等指令查看内核的加载情况。 !!! tip "遇到了奇怪的问题?尝试更改 `log::set_max_level(log::LevelFilter::Info);` 来调整日志输出的等级,以便你能够观察到更多的日志输出。" @@ -369,8 +371,8 @@ let ret = data.read(); 对于只读和只写的寄存器,你可以使用 `PortWriteOnly` 和 `PortReadOnly` 来从类型系统上防止误操作的发生。 -- 偏移量为 `1` 的寄存器是中断使能寄存器,可以使用 `PortWriteOnly::new(base + 1)` 操作。 -- 偏移量为 `5` 的寄存器是线控寄存器,可以使用 `PortReadOnly::new(base + 5)` 操作。 +- 偏移量为 `1` 的寄存器是中断使能寄存器,可以使用 `PortWriteOnly::new(base + 1)` 操作。 +- 偏移量为 `5` 的寄存器是线控寄存器,可以使用 `PortReadOnly::new(base + 5)` 操作。 对于串口设备,其寄存器均为 8 位,你可以使用 `u8` 类型来进行读写操作。 @@ -512,9 +514,9 @@ println!("{}", record.args()); 4. 在 QEMU 中,我们通过指定 `-nographic` 参数来禁用图形界面,这样 QEMU 会默认将串口输出重定向到主机的标准输出。 - - 假如我们将 `Makefile` 中取消该选项,QEMU 的输出窗口会发生什么变化?请观察指令 `make run QEMU_OUTPUT=` 的输出,结合截图分析对应现象。 - - 在移除 `-nographic` 的情况下,如何依然将串口重定向到主机的标准输入输出?请尝试自行构造命令行参数,并查阅 QEMU 的文档,进行实验。 - - 如果你使用 `ysos.py` 来启动 qemu,可以尝试修改 `-o` 选项来实现上述功能。 + - 假如我们将 `Makefile` 中取消该选项,QEMU 的输出窗口会发生什么变化?请观察指令 `make run QEMU_OUTPUT=` 的输出,结合截图分析对应现象。 + - 在移除 `-nographic` 的情况下,如何依然将串口重定向到主机的标准输入输出?请尝试自行构造命令行参数,并查阅 QEMU 的文档,进行实验。 + - 如果你使用 `ysos.py` 来启动 qemu,可以尝试修改 `-o` 选项来实现上述功能。 !!! note "现象观察提示" diff --git a/docs/labs/0x02/index.md b/docs/labs/0x02/index.md index 57d8813..c59ab1a 100644 --- a/docs/labs/0x02/index.md +++ b/docs/labs/0x02/index.md @@ -23,9 +23,9 @@ 对于本次实验内容,你需要参考学习如下实验资料: -- [CPU 异常与中断处理](../../wiki/interrupts.md) -- [APIC 可编程中断控制器](../../wiki/apic.md) -- [x64 数据结构概述](../../wiki/structures.md) +- [CPU 异常与中断处理](../../wiki/interrupts.md) +- [APIC 可编程中断控制器](../../wiki/apic.md) +- [x64 数据结构概述](../../wiki/structures.md) ## 实验任务与要求 diff --git a/docs/labs/0x02/tasks.md b/docs/labs/0x02/tasks.md index 266c3a6..1fac546 100644 --- a/docs/labs/0x02/tasks.md +++ b/docs/labs/0x02/tasks.md @@ -22,19 +22,19 @@ 在 `pkg/kernel/src/memory` 文件夹中,增量代码补充包含了如下的模块: -- `address.rs`:定义了物理地址到虚拟地址的转换函数,这一模块接受启动结构体提供的物理地址偏移,从而对物理地址进行转换。此部分内容在 lab 1 中已经有所涉及,你可以参考[完整的物理地址映射](https://os.phil-opp.com/paging-implementation/#map-the-complete-physical-memory)进行深入了解。 -- `frames.rs`:利用 bootloader 传入的内存布局进行物理内存帧分配,实现 x86_64 的 `FrameAllocator` trait。**本次实验中不会涉及,后续实验中会用到。** -- `gdt.rs`:定义 TSS 和 GDT,为内核提供内存段描述符和任务状态段。 -- `allocator.rs`:注册内核堆分配器,为内核堆分配提供能力。从而能够在内核中使用 `alloc` 提供的操作和数据结构,进行动态内存分配的操作,如 `Vec`、`String`、`Box` 等。 +- `address.rs`:定义了物理地址到虚拟地址的转换函数,这一模块接受启动结构体提供的物理地址偏移,从而对物理地址进行转换。此部分内容在 lab 1 中已经有所涉及,你可以参考[完整的物理地址映射](https://os.phil-opp.com/paging-implementation/#map-the-complete-physical-memory)进行深入了解。 +- `frames.rs`:利用 bootloader 传入的内存布局进行物理内存帧分配,实现 x86_64 的 `FrameAllocator` trait。**本次实验中不会涉及,后续实验中会用到。** +- `gdt.rs`:定义 TSS 和 GDT,为内核提供内存段描述符和任务状态段。 +- `allocator.rs`:注册内核堆分配器,为内核堆分配提供能力。从而能够在内核中使用 `alloc` 提供的操作和数据结构,进行动态内存分配的操作,如 `Vec`、`String`、`Box` 等。 !!! note "动态内存分配算法在这里不做要求,本次实验直接使用现有的库赋予内核堆分配能力。" 在 `pkg/kernel/src/interrupt` 文件夹中,增量代码补充包含了如下的模块: -- `apic`:有关 XAPIC、IOAPIC 和 LAPIC 的定义和实现。 -- `consts.rs`:有关于中断向量、IRQ 的常量定义。 -- `exceptions.rs`:包含了 CPU 异常的处理函数,并暴露 `register_idt` 用于注册 IDT。 -- `mod.rs`:定义了 `init` 函数,用于初始化中断系统,加载 IDT。 +- `apic`:有关 XAPIC、IOAPIC 和 LAPIC 的定义和实现。 +- `consts.rs`:有关于中断向量、IRQ 的常量定义。 +- `exceptions.rs`:包含了 CPU 异常的处理函数,并暴露 `register_idt` 用于注册 IDT。 +- `mod.rs`:定义了 `init` 函数,用于初始化中断系统,加载 IDT。 ## GDT 与 TSS @@ -179,7 +179,7 @@ impl XApic { 下面以部分操作为例讲解如何进行 APIC 的初始化。 -- 检测系统中是否存在 APIC,在 `x86_64` 中可以通过如下代码获知: +- 检测系统中是否存在 APIC,在 `x86_64` 中可以通过如下代码获知: ```rust CpuId::new().get_feature_info().map( @@ -187,7 +187,7 @@ impl XApic { ).unwrap_or(false) ``` -- 操作 SPIV 寄存器,启用 APIC 并设置 Spurious IRQ Vector。 +- 操作 SPIV 寄存器,启用 APIC 并设置 Spurious IRQ Vector。 查询文档可知,SPIV 寄存器的偏移量为 0xF0。其位描述如下: @@ -230,7 +230,7 @@ impl XApic { self.write(0xF0, spiv); ``` -- 设置 LVT 寄存器。 +- 设置 LVT 寄存器。 Local Vector Table 寄存器用于设置中断向量号和触发模式。它们的位描述如下: @@ -307,11 +307,11 @@ impl XApic { - - Vector 为中断向量号,当中断发生时,CPU 会跳转到中断向量表中对应处理程序执行。 - - DMode(Delivery Mode)为中断传递模式,本实验中不做理解要求。 - - DS(Delivery Status)为中断传递状态,只读。 - - M(Mask)为中断屏蔽位,取值为 1 表示中断已屏蔽。 - - TP(Timer Periodic Mode)为定时器周期模式,决定定时器周期触发还是仅触发一次。 + - Vector 为中断向量号,当中断发生时,CPU 会跳转到中断向量表中对应处理程序执行。 + - DMode(Delivery Mode)为中断传递模式,本实验中不做理解要求。 + - DS(Delivery Status)为中断传递状态,只读。 + - M(Mask)为中断屏蔽位,取值为 1 表示中断已屏蔽。 + - TP(Timer Periodic Mode)为定时器周期模式,决定定时器周期触发还是仅触发一次。 其余的位暂时不需要关注,如有兴趣可以参考 APIC 文档下的参考资料。 @@ -333,12 +333,12 @@ impl XApic { self.write(0x350, 1 << 16); // set Mask ``` -- 设置计时器相关寄存器。 +- 设置计时器相关寄存器。 APIC 中控制计时器的寄存器包括 TDCR、TICR 和 LVT Timer。其中,TDCR 用于设置分频系数,TICR 用于设置初始计数值。 - - TDCR(0x3E0) 的分频系数决定了总线时钟与计时器时钟的比例,也即计时器的计数频率。 - - TICR(0x380) 的初始计数值决定了计时器的计数周期,每当计数到 0 时,就会触发中断。 + - TDCR(0x3E0) 的分频系数决定了总线时钟与计时器时钟的比例,也即计时器的计数频率。 + - TICR(0x380) 的初始计数值决定了计时器的计数周期,每当计数到 0 时,就会触发中断。 分频系数和 TDCR 寄存器的取值关系如下表所示,第二比特总是为 0: @@ -356,7 +356,7 @@ impl XApic { self.write(0x380, 0x20000); // set initial count to 0x20000 ``` -- 清除错误状态寄存器。 +- 清除错误状态寄存器。 APIC 中的错误状态寄存器(Error Status Register, 0x280)用于记录 APIC 内部的错误状态。当 APIC 发生错误时,CPU 会将错误信息写入此寄存器。为了避免错误状态寄存器中的错误信息影响后续的错误处理,需要在初始化 APIC 时清除错误状态寄存器中的错误信息。 @@ -367,7 +367,7 @@ impl XApic { self.write(0x280, 0); ``` -- 设置 ICR 寄存器。 +- 设置 ICR 寄存器。 中断命令寄存器由两个 32 位寄存器组成,一个在 0x300,另一个在 0x310。它用于向不同的处理器发送中断。在写入 0x300 时发出中断,但在写入 0x310 时不发出中断。因此,要发送中断命令,应首先写入 0x310,然后写入 0x300。 @@ -423,10 +423,10 @@ impl XApic { 具体的配置配置细节这里不做理解要求,只需要按照如下描述进行配置即可: - - DSH(Destination Shorthand):设置为 2,始终将中断发送给所有 APIC - - DMode(Delivery Mode):设置为 5,INIT De-assert 模式 - - LV(Level):设置为 0,INIT De-assert 模式 - - TM(Trigger Mode):设置为 1,INIT De-assert 模式 + - DSH(Destination Shorthand):设置为 2,始终将中断发送给所有 APIC + - DMode(Delivery Mode):设置为 5,INIT De-assert 模式 + - LV(Level):设置为 0,INIT De-assert 模式 + - TM(Trigger Mode):设置为 1,INIT De-assert 模式 参考代码如下: diff --git a/docs/labs/0x03/tasks.md b/docs/labs/0x03/tasks.md index 03d5a19..a08d4dd 100644 --- a/docs/labs/0x03/tasks.md +++ b/docs/labs/0x03/tasks.md @@ -56,11 +56,11 @@ 在之前的实验中,已经描述过在 x86_64 架构下的中断发生时,CPU 会将当前的一部分上下文保存到内核栈中,然后跳转到中断处理函数。这些上下文包括: -- `instruction_pointer`:指令指针,保存了中断发生时 CPU 正在执行的指令的地址。 -- `code_segment`:代码段寄存器,保存了当前正在执行的代码段的选择子。 -- `cpu_flags`:CPU 标志寄存器,保存了中断前的 CPU 标志状态。 -- `stack_pointer`:栈指针,保存了中断前的栈指针。 -- `stack_segment`:栈段寄存器,保存了中断前的栈段选择子,在 x86_64 下总是为 0。 +- `instruction_pointer`:指令指针,保存了中断发生时 CPU 正在执行的指令的地址。 +- `code_segment`:代码段寄存器,保存了当前正在执行的代码段的选择子。 +- `cpu_flags`:CPU 标志寄存器,保存了中断前的 CPU 标志状态。 +- `stack_pointer`:栈指针,保存了中断前的栈指针。 +- `stack_segment`:栈段寄存器,保存了中断前的栈段选择子,在 x86_64 下总是为 0。 而在进行进程切换时,通常还需要保存和恢复更多的上下文,这些内容主要包括通用寄存器和浮点寄存器。为了简化实现,实验在编译架构选项中禁用了浮点寄存器,因此只需要保存和恢复通用寄存器即可。 @@ -251,29 +251,29 @@ pub fn new(init: Arc) -> Self { 在一次进程切换的过程中,需要**关闭中断**,之后完成以下几个步骤: -- 保存当前进程的上下文 +- 保存当前进程的上下文 经过上述进程上下文的描述可知,作为可变引用参数被传入的 `ProcessContext` 中存储了进程切换时需要保存的所有寄存器的值,并且其中的内容将会在进程切换完成后被恢复,进而真正实现进程的切换。 因此,进程切换的第一步就是将 `context` 保存至当前进程的 `ProcessInner` 中,以便下次恢复运行状态。 -- 更新当前进程的状态 +- 更新当前进程的状态 进程切换时,若它当前的状态并非 `Dead`,则当前进程的状态会被更新为 `Ready`。 同时,为了记录进程的执行时间,一般也会记录进程的调度次数,这里使用 `usize` 类型的 `ticks_passed` 字段来记录进程的调度次数。 -- 将当前进程放入就绪队列 +- 将当前进程放入就绪队列 当前进程被切换出去后,它会被放入就绪队列的末尾,等待下一次调度。 !!! tip "被调度的进程状态可能在就绪队列中时发生了改变,因此需要进行一些检查。" -- 从就绪队列中选取下一个进程 +- 从就绪队列中选取下一个进程 进程调度器会从就绪队列中选取第一个进程,检查进程的状态,如果进程处于可调度状态,就将其状态更新为 `Running`,并将其 PID 写入 `Processor` 中。 -- 切换进程上下文和页表 +- 切换进程上下文和页表 进程调度器会将选中的进程的上下文重新加载,并将新进程的页表物理地址写入 `Cr3` 寄存器,从而完成进程的切换。 @@ -283,17 +283,17 @@ pub fn new(init: Arc) -> Self { 在已经进行过的实验中,笔者为大家预设了一些内存布局,你应该或多或少接触过这些地址: -- 物理内存偏移:`0xFFFF800000000000` +- 物理内存偏移:`0xFFFF800000000000` 通过定义物理内存偏移,借助 2MB 的页映射,将物理内存线性映射到了这一偏移量所对应的的地址空间中。在内核中,当需要访问一个物理内存地址,如 `0x1000` 时,就可以通过 `0xFFFF800000001000` 来访问。 -- 内核空间起始地址:`0xFFFFFF0000000000` +- 内核空间起始地址:`0xFFFFFF0000000000` 在操作系统中,一般会将内核地址映射到高偏移,而将用户地址映射到低偏移。在实验中,内核空间的起始地址被定义在了 `0xFFFFFF0000000000`,这相当于为内核预留了 1TiB 的地址空间。 通过 `kernel.ld` 链接器脚本,将内核的起始地址设置为了这一地址,实验中编译的操作系统内核会被链接到从这一地址开始的地址空间中。同时,通过 bootloader 中的内核加载函数,读取 ELF 文件的描述,并将这些内容加载到了对应的地址空间中。 -- 内核栈地址:`0xFFFFFF0100000000` +- 内核栈地址:`0xFFFFFF0100000000` 内核栈的起始地址通过配置文件被定义在了 `0xFFFFFF0100000000`,距离内核起始地址 4GiB。默认大小为 512 个 4KiB 的页面,即 2MiB。 @@ -394,19 +394,19 @@ pub const STACK_INIT_TOP: u64 = STACK_MAX - 8; 在 `pkg/kernel/src/utils` 文件夹中,增量代码补充包含了如下的模块: -- `regs.rs`:对需要保存的一系列寄存器进行了封装,规定了其输出方式,补全了进程切换时需要使用的汇编代码及 `as_handler` 宏。 -- `func.rs`:定义了用于测试执行的两个函数,其中 `test` 用以验证进程调度、并发的正确性,`huge_stack` 用以验证处理缺页异常的正确性。 +- `regs.rs`:对需要保存的一系列寄存器进行了封装,规定了其输出方式,补全了进程切换时需要使用的汇编代码及 `as_handler` 宏。 +- `func.rs`:定义了用于测试执行的两个函数,其中 `test` 用以验证进程调度、并发的正确性,`huge_stack` 用以验证处理缺页异常的正确性。 在 `pkg/kernel/src/proc` 文件夹中,增量代码补充包含了如下的模块: -- `context.rs`:进程上下文的定义和实现,其中包含了加载、保存进程上下文的相关函数。 -- `data.rs`:进程数据结构体的定义,这里存储的数据在进程被杀死后会被释放,包含了使用 `Arc` 保护的线程间共享的数据(子进程相关内容将在下次实验中使用)。 -- `vm/{mod.rs,stack.rs}`:进程的虚拟内存管理,包含了栈空间的分配和释放函数,以及一些常量的定义。 -- `manager.rs`:进程管理器的定义和实现,时钟中断最终会通过进程管理器来进行任务切换。 -- `paging.rs`:进程页表的存储、切换所用数据,使用 `load` 函数加载进程页表到 `Cr3` 寄存器,使用 `clone` 函数来获得当前页表的副本,用于创建新进程。 -- `pid.rs`:使用元组结构体将一个 `u16` 作为进程 ID,需要为 `new` 函数确保获取唯一的 PID。 -- `processor.rs`:对处理器的抽象,使用 `AtomicU16` 来存储当前正在运行的进程的 PID,使用 `set_pid` 函数来设置当前进程的 PID,使用 `get_pid` 函数来获取当前进程的 PID。 -- `process.rs`:进程结构体的核心实现,包含了进程的状态、调度计数、退出返回值、父子关系、中断上下文等内容,是管理进程的核心模块。 +- `context.rs`:进程上下文的定义和实现,其中包含了加载、保存进程上下文的相关函数。 +- `data.rs`:进程数据结构体的定义,这里存储的数据在进程被杀死后会被释放,包含了使用 `Arc` 保护的线程间共享的数据(子进程相关内容将在下次实验中使用)。 +- `vm/{mod.rs,stack.rs}`:进程的虚拟内存管理,包含了栈空间的分配和释放函数,以及一些常量的定义。 +- `manager.rs`:进程管理器的定义和实现,时钟中断最终会通过进程管理器来进行任务切换。 +- `paging.rs`:进程页表的存储、切换所用数据,使用 `load` 函数加载进程页表到 `Cr3` 寄存器,使用 `clone` 函数来获得当前页表的副本,用于创建新进程。 +- `pid.rs`:使用元组结构体将一个 `u16` 作为进程 ID,需要为 `new` 函数确保获取唯一的 PID。 +- `processor.rs`:对处理器的抽象,使用 `AtomicU16` 来存储当前正在运行的进程的 PID,使用 `set_pid` 函数来设置当前进程的 PID,使用 `get_pid` 函数来获取当前进程的 PID。 +- `process.rs`:进程结构体的核心实现,包含了进程的状态、调度计数、退出返回值、父子关系、中断上下文等内容,是管理进程的核心模块。 !!! warning "利用好先前实现的日志系统和调试工具,帮助定位问题和实现功能" @@ -559,12 +559,12 @@ pub fn new_test_thread(id: &str) -> ProcessId { 在操作系统进行虚拟内存管理的时候经常会遇到缺页中断,作为可恢复的异常,它发生的可能性有很多: -- 内存页被标记为懒分配,只有当进程访问到这一页面时才会被分配。 -- 部分可执行的代码段尚未被加载到内存中,需要从磁盘文件进行加载。 -- 内存被交换到了磁盘上,再次使用需要交换回来。 -- 内存页面被标记为只读,在进程尝试写页面的时候触发了 COW(Copy on Write)机制,需要进行页面的复制。 -- 进程访问量权限不允许的内存区域,比如用户态进程尝试访问内核空间的内存。 -- …… +- 内存页被标记为懒分配,只有当进程访问到这一页面时才会被分配。 +- 部分可执行的代码段尚未被加载到内存中,需要从磁盘文件进行加载。 +- 内存被交换到了磁盘上,再次使用需要交换回来。 +- 内存页面被标记为只读,在进程尝试写页面的时候触发了 COW(Copy on Write)机制,需要进行页面的复制。 +- 进程访问量权限不允许的内存区域,比如用户态进程尝试访问内核空间的内存。 +- …… 在本实验设计中,并不会完全的实现上述的所有功能,只实现一个功能来作为缺页异常处理的示例:**为栈空间进行自动扩容。** diff --git a/docs/labs/0x04/index.md b/docs/labs/0x04/index.md index 495d8b0..e9941d3 100644 --- a/docs/labs/0x04/index.md +++ b/docs/labs/0x04/index.md @@ -23,7 +23,7 @@ 对于本次实验内容,你需要参考学习如下实验资料: -- [用户空间](../../wiki/userspace.md) +- [用户空间](../../wiki/userspace.md) ## 实验任务与要求 diff --git a/docs/labs/0x04/tasks.md b/docs/labs/0x04/tasks.md index a14b74c..c4eac80 100644 --- a/docs/labs/0x04/tasks.md +++ b/docs/labs/0x04/tasks.md @@ -34,9 +34,9 @@ 在 `pkg/kernel` 中,添加了如下一些模块: -- `interrupt/syscall`:定义系统调用及其服务的实现。 -- `memory/user`:用户堆内存分配的实现,会被用在系统调用的处理中,将用户态的内存分配委托给内核。 -- `utils/resource`:定义了用于进行 I/O 操作的 `Resource` 结构体,用于处理用户态的读写系统调用。 +- `interrupt/syscall`:定义系统调用及其服务的实现。 +- `memory/user`:用户堆内存分配的实现,会被用在系统调用的处理中,将用户态的内存分配委托给内核。 +- `utils/resource`:定义了用于进行 I/O 操作的 `Resource` 结构体,用于处理用户态的读写系统调用。 !!! tip "别忘了更新 `Cargo.toml`" @@ -60,9 +60,11 @@ ```rust [dependencies] -lib = { path="../../lib", package="yslib"} +lib = { workspace = true } ``` +!!! tip "在根目录给出的 `Cargo.toml` 中,为你准备了目标项目所需要的全部工作区依赖列表,你可以按需引用、删除、添加你需要的依赖" + 一个简单的用户程序示例如下所示,同样存在于 `app/hello/src/main.rs` 中: ```rust @@ -82,8 +84,8 @@ fn main() -> isize { entry!(main); ``` -- `#![no_std]` 表示不使用标准库,rust 并没有支持 YSOS 的标准库,需要我们自行实现。 -- `#![no_main]` 表示不使用标准的 `main` 函数入口,而是使用 `entry!` 宏定义的入口函数。 +- `#![no_std]` 表示不使用标准库,rust 并没有支持 YSOS 的标准库,需要我们自行实现。 +- `#![no_main]` 表示不使用标准的 `main` 函数入口,而是使用 `entry!` 宏定义的入口函数。 `entry!` 宏的定义如下: @@ -439,10 +441,10 @@ pub fn init_stack_frame(&mut self, entry: VirtAddr, stack_top: VirtAddr) { 以 x86_64 的 Linux 为例,系统调用的部分调用约定如下所示: -- 系统调用号通过 `rax` 寄存器传递 -- 参数通过 `rdi`、`rsi`、`rdx`、`r10`、`r8`、`r9` 寄存器传递 -- 参数数量大于 6 时,通过栈传递 -- 返回值通过 `rax` 寄存器传递 +- 系统调用号通过 `rax` 寄存器传递 +- 参数通过 `rdi`、`rsi`、`rdx`、`r10`、`r8`、`r9` 寄存器传递 +- 参数数量大于 6 时,通过栈传递 +- 返回值通过 `rax` 寄存器传递 !!! tip "在系统调用中,由于 `rcx` 寄存器有其他用途,因此使用 `r10` 寄存器代替函数调用约定 `__fastcall` 中的 `rcx` 寄存器。" @@ -500,9 +502,9 @@ pub enum Syscall { 由于一些额外的执念,这里的读写、进程操作的系统调用号基本与 Linux 中功能类似的系统调用号一致,而有些系统调用号则是自定义的。 -- `ListApp` 用于列出当前系统中的所有用户程序,由于尚不会进行文件系统的实现,因此需要这样一个系统调用来获取用户程序的信息。 -- `Stat` 用于获取系统中的一些统计信息,例如内存使用情况、进程列表等,用于调试和监控。 -- `Allocate/Deallocate` 用于分配和释放内存。在当前没有完整的用户态内存分配支持的情况下,可以利用系统调用将其委托给内核来完成。 +- `ListApp` 用于列出当前系统中的所有用户程序,由于尚不会进行文件系统的实现,因此需要这样一个系统调用来获取用户程序的信息。 +- `Stat` 用于获取系统中的一些统计信息,例如内存使用情况、进程列表等,用于调试和监控。 +- `Allocate/Deallocate` 用于分配和释放内存。在当前没有完整的用户态内存分配支持的情况下,可以利用系统调用将其委托给内核来完成。 ### 软中断处理 @@ -843,15 +845,15 @@ Syscall::WaitPid => { /* ... */}, 至此,你可以编写自己的 Shell 了!作为用户与操作系统的交互方式,它需要实现一些必须功能: -- 列出当前系统中的所有用户程序 -- 列出当前正在运行的全部进程 -- 运行一个用户程序 +- 列出当前系统中的所有用户程序 +- 列出当前正在运行的全部进程 +- 运行一个用户程序 同时,它也可以实现一些辅助的能力: -- 列出帮助信息 -- 清空屏幕 -- ... +- 列出帮助信息 +- 清空屏幕 +- ... 为了实现一些信息的查看,你也需要实现如下两个系统调用: diff --git a/docs/labs/0x05/tasks.md b/docs/labs/0x05/tasks.md index dea0678..1cc49e9 100644 --- a/docs/labs/0x05/tasks.md +++ b/docs/labs/0x05/tasks.md @@ -20,11 +20,11 @@ YSOS 的 `fork` 系统调用设计如下描述: !!! note "出于实验设计考量:
本实现与 Linux 或 [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/) 中所定义的 `fork` 有所不同,也结合了 Linux 中 `vfork` 的行为。" -- `fork` 会创建一个新的进程,新进程称为子进程,原进程称为父进程。 -- **子进程在系统调用后将得到 `0` 的返回值,而父进程将得到子进程的 PID。** 如果创建失败,父进程将得到 `-1` 的返回值。 -- `fork` **不复制**父进程的内存空间,**不实现** Cow (Copy on Write) 机制,即父子进程将持有一定的共享内存:代码段、数据段、堆、bss 段等。 -- `fork` 子进程与父进程共享内存空间(页表),但**子进程拥有自己独立的寄存器和栈空间,即在一个不同的栈的地址继承原来的数据。** -- **由于上述内存分配机制的限制,`fork` 系统调用必须在任何 Rust 内存分配(堆内存分配)之前进行。** +- `fork` 会创建一个新的进程,新进程称为子进程,原进程称为父进程。 +- **子进程在系统调用后将得到 `0` 的返回值,而父进程将得到子进程的 PID。** 如果创建失败,父进程将得到 `-1` 的返回值。 +- `fork` **不复制**父进程的内存空间,**不实现** Cow (Copy on Write) 机制,即父子进程将持有一定的共享内存:代码段、数据段、堆、bss 段等。 +- `fork` 子进程与父进程共享内存空间(页表),但**子进程拥有自己独立的寄存器和栈空间,即在一个不同的栈的地址继承原来的数据。** +- **由于上述内存分配机制的限制,`fork` 系统调用必须在任何 Rust 内存分配(堆内存分配)之前进行。** 为了实现父子进程的资源共享,在先前的实验中,已经做了一些准备工作: @@ -168,9 +168,9 @@ impl Stack { 1. 将功能的具体实现委托至下一级进行,保持代码语义的简洁。 - - 系统调用静态函数,并将其委托给 `ProcessManager::fork`。 - - `ProcessManager::fork` 将具体实现委托给当前进程的 `Process::fork`。 - - `Process::fork` 将具体实现委托给 `ProcessInner::fork`。 + - 系统调用静态函数,并将其委托给 `ProcessManager::fork`。 + - `ProcessManager::fork` 将具体实现委托给当前进程的 `Process::fork`。 + - `Process::fork` 将具体实现委托给 `ProcessInner::fork`。 每一层代码只关心自己层级的逻辑和数据,不关心持有自身的锁或其他外部数据的状态,进而提高代码可维护性。 @@ -635,9 +635,9 @@ unsafe impl Sync for SpinLock {} 在实现 `acquire` 和 `release` 时,你需要使用 `AtomicBool` 的原子操作来保证锁的正确性: -- `load` 函数用于读取当前值。 -- `store` 函数用于设置新值。 -- `compare_exchange` 函数用于原子地进行比较-交换,也即比较当前值是否为目标值,如果是则将其设置为新值,否则返回当前值。 +- `load` 函数用于读取当前值。 +- `store` 函数用于设置新值。 +- `compare_exchange` 函数用于原子地进行比较-交换,也即比较当前值是否为目标值,如果是则将其设置为新值,否则返回当前值。 在进行循环等待时,可以使用 `core::hint::spin_loop` 提高性能,在 x86_64 架构中,它实际上会编译为 `pause` 指令。 @@ -645,18 +645,18 @@ unsafe impl Sync for SpinLock {} 得利于 Rust 良好的底层封装,自旋锁的实现非常简单。但是也存在一定的问题: -- 忙等待:自旋锁会一直占用 CPU 时间,直到获取到锁为止,这会导致 CPU 利用率的下降。 -- 饥饿:如果一个线程一直占用锁,其他线程可能会一直无法获取到锁。 -- 死锁:如果两个线程互相等待对方占有的锁,就会导致死锁。 +- 忙等待:自旋锁会一直占用 CPU 时间,直到获取到锁为止,这会导致 CPU 利用率的下降。 +- 饥饿:如果一个线程一直占用锁,其他线程可能会一直无法获取到锁。 +- 死锁:如果两个线程互相等待对方占有的锁,就会导致死锁。 信号量 `Semaphore` 是一种更为复杂的同步机制,它可以用于控制对共享资源的访问,也可以用于控制对临界区的访问。通过与进程调度相关的操作,信号量还可以用于控制进程的执行顺序、提高 CPU 利用率等。 信号量需要实现四种操作: -- `new`:根据所给出的 `key` 创建一个新的信号量。 -- `remove`:根据所给出的 `key` 删除一个已经存在的信号量。 -- `signal`:也叫做 `V` 操作,也可以被 `release/up/verhogen` 表示,它用于释放一个资源,使得等待的进程可以继续执行。 -- `wait`:也叫做 `P` 操作,也可以被 `acquire/down/proberen` 表示,它用于获取一个资源,如果资源不可用,则进程将会被阻塞。 +- `new`:根据所给出的 `key` 创建一个新的信号量。 +- `remove`:根据所给出的 `key` 删除一个已经存在的信号量。 +- `signal`:也叫做 `V` 操作,也可以被 `release/up/verhogen` 表示,它用于释放一个资源,使得等待的进程可以继续执行。 +- `wait`:也叫做 `P` 操作,也可以被 `acquire/down/proberen` 表示,它用于获取一个资源,如果资源不可用,则进程将会被阻塞。 为了实现与内核的交互,信号量的操作将被实现为一个系统调用,它将使用到三个系统调用参数: @@ -689,10 +689,10 @@ pub enum SemaphoreResult { } ``` -- `Ok`:表示操作成功,且无需进行阻塞或唤醒。 -- `NotExist`:表示信号量不存在。 -- `Block(ProcessId)`:表示操作需要阻塞线程,一般是当前进程。 -- `WakeUp(ProcessId)`:表示操作需要唤醒线程。 +- `Ok`:表示操作成功,且无需进行阻塞或唤醒。 +- `NotExist`:表示信号量不存在。 +- `Block(ProcessId)`:表示操作需要阻塞线程,一般是当前进程。 +- `WakeUp(ProcessId)`:表示操作需要唤醒线程。 为了实现信号量的 KV 存储,使用 `SemaphoreSet` 定义信号量集合的操作: @@ -811,30 +811,30 @@ pub fn sys_sem(args: &SyscallArgs, context: &mut ProcessContext) { 创建一个用户程序 `pkg/app/mq`,结合使用信号量,实现一个消息队列: -- 父进程使用 fork 创建额外的 16 个进程,其中一半为生产者,一半为消费者。 +- 父进程使用 fork 创建额外的 16 个进程,其中一半为生产者,一半为消费者。 -- 生产者不断地向消息队列中写入消息,消费者不断地从消息队列中读取消息。 +- 生产者不断地向消息队列中写入消息,消费者不断地从消息队列中读取消息。 -- 每个线程处理的消息总量共 10 条。 +- 每个线程处理的消息总量共 10 条。 即生产者会产生 10 个消息,每个消费者只消费 10 个消息。 -- 在每个线程生产或消费的时候,输出相关的信息。 +- 在每个线程生产或消费的时候,输出相关的信息。 你可能需要使用信号量或旋锁来实现一个互斥锁,保证操作和信息输出之间不会被打断。 -- 在生产者和消费者完成上述操作后,使用 `sys_exit(0)` 直接退出。 +- 在生产者和消费者完成上述操作后,使用 `sys_exit(0)` 直接退出。 -- 最终使用父进程等待全部的子进程退出后,输出消息队列的消息数量。 +- 最终使用父进程等待全部的子进程退出后,输出消息队列的消息数量。 -- 在父进程创建完成 16 个进程后,使用 `sys_stat` 输出当前的全部进程的信息。 +- 在父进程创建完成 16 个进程后,使用 `sys_stat` 输出当前的全部进程的信息。 你需要保证最终消息队列中的消息数量为 0,你可以开启内核更加详细的日志,并使用输出的相关信息尝试证明队列的正常工作: -- 在从队列取出消息时,消息为空吗? -- 在向队列写入消息时,队列是否满了? -- 在队列为空时,消费者是否被阻塞? -- 在队列满时,生产者是否被阻塞? +- 在从队列取出消息时,消息为空吗? +- 在向队列写入消息时,队列是否满了? +- 在队列为空时,消费者是否被阻塞? +- 在队列满时,生产者是否被阻塞? !!! question "分别设置队列容量为 `1, 4, 8, 16`,记录观察生产者和消费者的行为:" @@ -852,22 +852,22 @@ pub fn sys_sem(args: &SyscallArgs, context: &mut ProcessContext) { 创建一个用户程序 `pkg/app/dinner`,使用课上学到的知识,实现并解决哲学家就餐问题: -- 创建一个程序,模拟五个哲学家的行为。 +- 创建一个程序,模拟五个哲学家的行为。 -- 每个哲学家都是一个独立的线程,可以同时进行思考和就餐。 +- 每个哲学家都是一个独立的线程,可以同时进行思考和就餐。 -- 使用互斥锁来保护每个筷子,确保同一时间只有一个哲学家可以拿起一根筷子。 +- 使用互斥锁来保护每个筷子,确保同一时间只有一个哲学家可以拿起一根筷子。 -- 使用等待操作调整哲学家的思考和就餐时间,以增加并发性和实际性。 +- 使用等待操作调整哲学家的思考和就餐时间,以增加并发性和实际性。 - - 如果你实现了 `sys_time` 系统调用(Lab 4),可以使用它来构造 `sleep` 操作。 - - 如果你并没有实现它,可以参考多线程计数器中的 `delay` 函数进行实现。 + - 如果你实现了 `sys_time` 系统调用(Lab 4),可以使用它来构造 `sleep` 操作。 + - 如果你并没有实现它,可以参考多线程计数器中的 `delay` 函数进行实现。 -- 当哲学家成功就餐时,输出相关信息,如哲学家编号、就餐时间等。 +- 当哲学家成功就餐时,输出相关信息,如哲学家编号、就餐时间等。 -- 向程序中引入一些随机性,例如在尝试拿筷子时引入一定的延迟,模拟竞争条件和资源争用。 +- 向程序中引入一些随机性,例如在尝试拿筷子时引入一定的延迟,模拟竞争条件和资源争用。 -- 可以设置等待时间或循环次数,以确保程序能够运行足够长的时间,并尝试观察到不同的情况,如死锁和饥饿。 +- 可以设置等待时间或循环次数,以确保程序能够运行足够长的时间,并尝试观察到不同的情况,如死锁和饥饿。 ??? tip "在用户态中引入伪随机数" @@ -906,9 +906,9 @@ pub fn sys_sem(args: &SyscallArgs, context: &mut ProcessContext) { 通过观察程序的输出和行为,请尝试构造并截图记录以下现象: -- 某些哲学家能够成功就餐,即同时拿到左右两侧的筷子。 -- 尝试构造死锁情况,即所有哲学家都无法同时拿到他们需要的筷子。 -- 尝试构造饥饿情况,即某些哲学家无法获得足够的机会就餐。 +- 某些哲学家能够成功就餐,即同时拿到左右两侧的筷子。 +- 尝试构造死锁情况,即所有哲学家都无法同时拿到他们需要的筷子。 +- 尝试构造饥饿情况,即某些哲学家无法获得足够的机会就餐。 尝试解决上述可能存在的问题,并介绍你的解决思路。 @@ -944,8 +944,8 @@ pub fn sys_sem(args: &SyscallArgs, context: &mut ProcessContext) { 1. 🤔 尝试实现如下用户程序任务,完成用户程序 `fish`: - - 创建三个子进程,让它们分别能输出且只能输出 `>`,`<` 和 `_`。 - - 使用学到的方法对这些子进程进行同步,使得打印出的序列总是 `<><_` 和 `><>_` 的组合。 + - 创建三个子进程,让它们分别能输出且只能输出 `>`,`<` 和 `_`。 + - 使用学到的方法对这些子进程进行同步,使得打印出的序列总是 `<><_` 和 `><>_` 的组合。 在完成这一任务的基础上,其他细节可以自行决定如何实现,包括输出长度等。 @@ -957,8 +957,8 @@ pub fn sys_sem(args: &SyscallArgs, context: &mut ProcessContext) { 对于 Rust,也即实现 `MutexGuard` 类似的结构,它在构造时获取锁,然后在此结构体被移出作用域时释放锁。 - - 在 `acquire` 时候返回 `MutexGuard` 对象。 - - 移除 `release` 函数,使用 `MutexGuard` 的 `Drop` trait 来释放锁。 + - 在 `acquire` 时候返回 `MutexGuard` 对象。 + - 移除 `release` 函数,使用 `MutexGuard` 的 `Drop` trait 来释放锁。 !!! danger "本项实现难度较大,不建议初学者尝试。" diff --git a/docs/labs/0x06/index.md b/docs/labs/0x06/index.md index 9274172..02f405a 100644 --- a/docs/labs/0x06/index.md +++ b/docs/labs/0x06/index.md @@ -23,9 +23,9 @@ 对于本次实验内容,你需要参考学习如下实验资料: -- [文件系统概述](../../wiki/fs.md) -- [ATA 硬盘简介](../../wiki/ata.md) -- [FAT 文件系统](../../wiki/fat.md) +- [文件系统概述](../../wiki/fs.md) +- [ATA 硬盘简介](../../wiki/ata.md) +- [FAT 文件系统](../../wiki/fat.md) ## 实验任务与要求 diff --git a/docs/labs/0x06/tasks.md b/docs/labs/0x06/tasks.md index 71d54dc..862fb9d 100644 --- a/docs/labs/0x06/tasks.md +++ b/docs/labs/0x06/tasks.md @@ -26,17 +26,17 @@ 在 `pkg/storage/src/common` 中,提供了众多有关存储的底层结构: -- `block.rs`: 提供了数据块的抽象,用于存储数据,内部为指定大小的 `u8` 数组。 -- `device.rs`: 目前只提供了块设备的抽象,提供分块读取数据的接口。 -- `error.rs`: 定义了文件系统、磁盘、文件名可能遇到的一系列错误,并定义了以 `FsError` 为错误类型的 `Result`。 -- `filesystem.rs`: 定义了文件系统的抽象,提供了文件系统的基本操作接口。 -- `io.rs`: 定义了 `Read`、`Write` 和 `Seek` 的行为,不过在本次实验中只需要实现 `Read`。 -- `metadata.rs`:定义了统一的文件元信息,包含文件名、修改时间、大小等信息。 +- `block.rs`: 提供了数据块的抽象,用于存储数据,内部为指定大小的 `u8` 数组。 +- `device.rs`: 目前只提供了块设备的抽象,提供分块读取数据的接口。 +- `error.rs`: 定义了文件系统、磁盘、文件名可能遇到的一系列错误,并定义了以 `FsError` 为错误类型的 `Result`。 +- `filesystem.rs`: 定义了文件系统的抽象,提供了文件系统的基本操作接口。 +- `io.rs`: 定义了 `Read`、`Write` 和 `Seek` 的行为,不过在本次实验中只需要实现 `Read`。 +- `metadata.rs`:定义了统一的文件元信息,包含文件名、修改时间、大小等信息。 同时,有了接口定义了统一的行为之后,可以利用他们来实现具有更丰富功能的结构体: -- `filehandle.rs`: 定义了文件句柄,它持有一个实现了 `FileIO` trait 的字段,并维护了文件的元数据。 -- `mount.rs`: 定义了挂载点,它持有一个实现了 `Filesystem` trait 的字段,并维护了一个固定的挂载点路径,它会将挂载点路径下的文件操作请求转发给内部的文件系统。 +- `filehandle.rs`: 定义了文件句柄,它持有一个实现了 `FileIO` trait 的字段,并维护了文件的元数据。 +- `mount.rs`: 定义了挂载点,它持有一个实现了 `Filesystem` trait 的字段,并维护了一个固定的挂载点路径,它会将挂载点路径下的文件操作请求转发给内部的文件系统。 在 `pkg/storage/src/partition/mod.rs` 中,定义了 `Partition` 结构体,和 `PartitionTable` trait,用于统一块设备的分区表行为。 @@ -119,10 +119,10 @@ storage = { package = "ysos_storage", path = "../storage" } 回顾一下之前编写串口驱动的过程,它与即将实现的驱动类似,都是 PIO 来进行数据传输: -- 根据规范定义端口,使用端口进行读写操作控制外设寄存器 -- 按照规定修改外设寄存器,使得设备按照预期的方式运行 -- 通过数据和状态寄存器,实现数据的发送和接收 -- 通过启用设备的中断,实现异步的数据传输(与轮询方式相对) +- 根据规范定义端口,使用端口进行读写操作控制外设寄存器 +- 按照规定修改外设寄存器,使得设备按照预期的方式运行 +- 通过数据和状态寄存器,实现数据的发送和接收 +- 通过启用设备的中断,实现异步的数据传输(与轮询方式相对) 在 [ATA 硬盘简介](../../wiki/ata.md) 中,介绍了 ATA 硬盘的基本工作原理,以及相关概念。 @@ -260,30 +260,30 @@ storage = { package = "ysos_storage", path = "../storage" } 在实现了上述文件系统的数据格式之后,你需要在 `fs/fat16/impls.rs` 中实现你需要的一系列函数,包括但不限于: -- 将 `cluster: &Cluster` 转换为 `sector` -- 根据当前 `cluster: &Cluster`,利用 FAT 表,获取下一个 `cluster` -- 根据当前文件夹 `dir: &Directory` 信息,获取名字为 `name: &str` 的 `DirEntry` -- 遍历文件夹 `dir: &Directory`,获取其中文件信息 -- 其他你可能需要的帮助函数 +- 将 `cluster: &Cluster` 转换为 `sector` +- 根据当前 `cluster: &Cluster`,利用 FAT 表,获取下一个 `cluster` +- 根据当前文件夹 `dir: &Directory` 信息,获取名字为 `name: &str` 的 `DirEntry` +- 遍历文件夹 `dir: &Directory`,获取其中文件信息 +- 其他你可能需要的帮助函数 在实现了一系列函数后,为 `impl FileSystem for Fat16` 补全实现: -- `Iterator` 可以简单利用 `Vec::into_iter` 作为返回值,不需要考虑懒求值。 -- `FileHandle` 的 `file` 部分直接使用 `fs/fat16/file.rs` 中定义的 `File` 结构体,并使用 `Box` 包装。 +- `Iterator` 可以简单利用 `Vec::into_iter` 作为返回值,不需要考虑懒求值。 +- `FileHandle` 的 `file` 部分直接使用 `fs/fat16/file.rs` 中定义的 `File` 结构体,并使用 `Box` 包装。 最后,为 `File` 实现 `Read` trait,需要注意: -- `cluster` 链需要使用上述函数读取 FAT 表进行获取。 -- `offset` 用于记录当前文件读取到了什么位置,需要实时更新。 -- 一个 `cluster` 中存在多个 `sector`,你需要根据 `bpb` 信息进行读取操作。 -- `buf` 参数是不定长的,你需要考虑文件长度、块长度以及缓冲区长度,来决定什么时候终止读取。 +- `cluster` 链需要使用上述函数读取 FAT 表进行获取。 +- `offset` 用于记录当前文件读取到了什么位置,需要实时更新。 +- 一个 `cluster` 中存在多个 `sector`,你需要根据 `bpb` 信息进行读取操作。 +- `buf` 参数是不定长的,你需要考虑文件长度、块长度以及缓冲区长度,来决定什么时候终止读取。 除此之外,本部分的实现不作任何要求,阅读并理解给出的 [FAT 文件系统](../../wiki/fat.md) 内容,尝试实现文件系统的功能。 同时,由于文件系统的实现相对较为严格,笔者鼓励大家多多查找相关已有实现,参考完善自己的文件系统,下面给出几个可供参考的仓库: -- [embedded-sdmmc-rs](https://github.com/rust-embedded-community/embedded-sdmmc-rs) -- [rust-fatfs](https://github.com/rafalh/rust-fatfs) +- [embedded-sdmmc-rs](https://github.com/rust-embedded-community/embedded-sdmmc-rs) +- [rust-fatfs](https://github.com/rafalh/rust-fatfs) ## 接入操作系统 diff --git a/docs/labs/0x07/tasks.md b/docs/labs/0x07/tasks.md index acc293e..77ccbe9 100644 --- a/docs/labs/0x07/tasks.md +++ b/docs/labs/0x07/tasks.md @@ -20,8 +20,8 @@ 本次实验代码量较小,给出的代码集中于 `pkg/kernel/src/proc/vm` 目录下。 -- `heap.rs`:添加了 `Heap` 结构体,用于管理堆内存。 -- `mod.rs`:除栈外,添加了堆内存、ELF 文件映射的初始化和清理函数。 +- `heap.rs`:添加了 `Heap` 结构体,用于管理堆内存。 +- `mod.rs`:除栈外,添加了堆内存、ELF 文件映射的初始化和清理函数。 !!! note "关于 `ProcessVm` 的角色" @@ -154,11 +154,11 @@ ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] 其中所示的内存区域: -- `/usr/bin/cat`:ELF 文件映射的内存区域,这里包含了 `cat` 程序的 `.text`、`.data`、`.bss` 等段。 -- `[heap]`:堆区的内存区域,这里包含了 `cat` 程序的堆内存,使用 `brk` 系统调用进行分配。 -- `/usr/lib/x86_64-linux-gnu/*.so.*`:动态链接库的内存区域,这里包含了程序使用的动态链接库的 `.text`、`.data`、`.bss` 等段。 -- `[stack]`:栈区的内存区域,这里包含了程序的栈内存,在达到栈区的最大大小前,栈区会自动增长。 -- `[vvar]`、`[vdso]`、`[vsyscall]`:内核的内存区域,这里包含了内核相关的一些数据结构,如 `vvar` 包含了一些内核和用户空间之间共享的变量;`vdso` 是虚拟的动态共享对象 (Virtual Dynamic Shared Object) 区域,用于在用户空间和内核空间之间提供一些系统调用的快速访问。 +- `/usr/bin/cat`:ELF 文件映射的内存区域,这里包含了 `cat` 程序的 `.text`、`.data`、`.bss` 等段。 +- `[heap]`:堆区的内存区域,这里包含了 `cat` 程序的堆内存,使用 `brk` 系统调用进行分配。 +- `/usr/lib/x86_64-linux-gnu/*.so.*`:动态链接库的内存区域,这里包含了程序使用的动态链接库的 `.text`、`.data`、`.bss` 等段。 +- `[stack]`:栈区的内存区域,这里包含了程序的栈内存,在达到栈区的最大大小前,栈区会自动增长。 +- `[vvar]`、`[vdso]`、`[vsyscall]`:内核的内存区域,这里包含了内核相关的一些数据结构,如 `vvar` 包含了一些内核和用户空间之间共享的变量;`vdso` 是虚拟的动态共享对象 (Virtual Dynamic Shared Object) 区域,用于在用户空间和内核空间之间提供一些系统调用的快速访问。 你也可以查看程序的内存使用情况,使用 `cat /proc//status`,依旧以 `cat` 程序为例: @@ -182,12 +182,12 @@ VmSwap: 0 kB 其中有几个需要注意的字段: -- `VmPeak` / `VmSize`:进程的峰值虚拟内存大小和当前虚拟内存大小,指的是整个虚拟内存空间的大小。 -- `VmHWM` / `VmRSS`:进程的峰值物理内存大小和当前物理内存大小,指的是进程实际使用的物理内存大小。 -- `RssAnon` / `RssFile` / `RssShmem`:进程的匿名内存、文件映射内存和共享内存的大小。 -- `VmData` / `VmStk` / `VmExe`:进程的数据段、栈段、代码段的大小。 -- `VmLib`:进程的动态链接库的大小,对于 `cat` 程序来说,这里主要是 `libc` 的占用,但这部分内存可以被多个进程很好地共享。 -- `VmPTE`:进程的页表项的大小。 +- `VmPeak` / `VmSize`:进程的峰值虚拟内存大小和当前虚拟内存大小,指的是整个虚拟内存空间的大小。 +- `VmHWM` / `VmRSS`:进程的峰值物理内存大小和当前物理内存大小,指的是进程实际使用的物理内存大小。 +- `RssAnon` / `RssFile` / `RssShmem`:进程的匿名内存、文件映射内存和共享内存的大小。 +- `VmData` / `VmStk` / `VmExe`:进程的数据段、栈段、代码段的大小。 +- `VmLib`:进程的动态链接库的大小,对于 `cat` 程序来说,这里主要是 `libc` 的占用,但这部分内存可以被多个进程很好地共享。 +- `VmPTE`:进程的页表项的大小。 当使用 `ps aux` 查看进程时,你可以看到更多的信息,如进程的 CPU 占用、内存占用、进程的状态等。 @@ -425,8 +425,8 @@ pub fn fork(&self) -> Self { } ``` -- `clone_level_4` 用于复制当前页表(仅第四级页表),并将其作为一个新的页表上下文返回,用于 `spawn` 时复制内核页表; -- `fork` 则是直接对 `Arc` 进行 `clone` 操作,使得新的进程与父进程共享页表。 +- `clone_level_4` 用于复制当前页表(仅第四级页表),并将其作为一个新的页表上下文返回,用于 `spawn` 时复制内核页表; +- `fork` 则是直接对 `Arc` 进行 `clone` 操作,使得新的进程与父进程共享页表。 也即,在目前的实现中,对于每一棵独立的“进程树”,它们的页表是独立的,但是在同一棵“进程树”中,它们的页表是共享的。 @@ -637,8 +637,8 @@ pub fn init_kernel_vm(mut self, pages: &KernelPages) -> Self { 需要用到的配置项在 Lab 1 中已经给出,即 `kernel_stack_auto_grow`,对它的行为进行如下约定: -- 默认为 `0`,这时内核栈区所需的全部页面(页面数量为 `kernel_stack_size`)将会在内核加载时一次性分配。 -- 当这一参数为非零值时,表示内核栈区的初始化页面数量,从栈顶开始向下分配这一数量的初始化页面,并交由内核进行自己的栈区管理。 +- 默认为 `0`,这时内核栈区所需的全部页面(页面数量为 `kernel_stack_size`)将会在内核加载时一次性分配。 +- 当这一参数为非零值时,表示内核栈区的初始化页面数量,从栈顶开始向下分配这一数量的初始化页面,并交由内核进行自己的栈区管理。 ```rust let (stack_start, stack_size) = if config.kernel_stack_auto_grow > 0 { @@ -816,16 +816,16 @@ pub fn brk(addr: Option) -> Option { 最终,你需要在 `pkg/kernel/src/proc/vm/heap.rs` 中为 `Heap` 结构体实现 `brk` 函数: -- 如果参数为 `None`,则表示用户程序希望获取当前的堆区结束地址,即返回 `end` 的值; -- 如果参数不为 `None`,则检查用户传入的目标地址是否合法,即是否在 `[HEAP_START, HEAP_END]` 区间内,如果不合法,直接返回 `None`。 +- 如果参数为 `None`,则表示用户程序希望获取当前的堆区结束地址,即返回 `end` 的值; +- 如果参数不为 `None`,则检查用户传入的目标地址是否合法,即是否在 `[HEAP_START, HEAP_END]` 区间内,如果不合法,直接返回 `None`。 如果目标地址合法,则按照以下约定处理: -- 初始化堆区时,`base` 和 `end` 的值均为 `HEAP_START`; -- 用户希望释放整个堆区:目标地址为 `base`,释放所有页面,`end` 重置为 `base`; -- 用户希望缩小堆区:目标地址比当前 `end` 小,先将目标地址向上对齐到页边界,然后释放多余的页面; -- 用户希望扩大堆区:目标地址比当前 `end` 大,先将目标地址向上对齐到页边界,然后分配的页面; -- 若调整成功,返回新的堆区终止地址;否则返回 `None`。 +- 初始化堆区时,`base` 和 `end` 的值均为 `HEAP_START`; +- 用户希望释放整个堆区:目标地址为 `base`,释放所有页面,`end` 重置为 `base`; +- 用户希望缩小堆区:目标地址比当前 `end` 小,先将目标地址向上对齐到页边界,然后释放多余的页面; +- 用户希望扩大堆区:目标地址比当前 `end` 大,先将目标地址向上对齐到页边界,然后分配的页面; +- 若调整成功,返回新的堆区终止地址;否则返回 `None`。 用户程序中,一段典型的调整堆的系统调用过程,可以参考如下代码: diff --git a/docs/wiki/apic.md b/docs/wiki/apic.md index 98aacb7..e5edf94 100644 --- a/docs/wiki/apic.md +++ b/docs/wiki/apic.md @@ -18,42 +18,42 @@ x2APIC 是 xAPIC 的变体和扩展,主要改进解决了支持的 CPU 数量 APIC 的初始化过程基本包括以下几个步骤: -- 禁用 8259 PIC,使得系统只使用 APIC 进行中断处理。 +- 禁用 8259 PIC,使得系统只使用 APIC 进行中断处理。 这一步被 UEFI BIOS 自动完成,我们无需关心。 -- 检测系统中是否存在 APIC。 +- 检测系统中是否存在 APIC。 -- 确定 APIC 的地址空间,即 LAPIC 和 IOAPIC 的 MMIO 地址空间。 +- 确定 APIC 的地址空间,即 LAPIC 和 IOAPIC 的 MMIO 地址空间。 由于我们采用了虚拟地址空间,所以这里需要将物理地址映射到虚拟地址空间中,之后再进行 APIC 的相关操作。 -- 操作 SPIV(Spurious Interrupt Vector Register, 0xF0)寄存器,启用 APIC 并设置 Spurious IRQ Vector。 +- 操作 SPIV(Spurious Interrupt Vector Register, 0xF0)寄存器,启用 APIC 并设置 Spurious IRQ Vector。 -- 设置计时器相关寄存器: +- 设置计时器相关寄存器: - - TDCR(0x3E0): Divide Configuration Register,设置分频系数。 - - TICR(0x380): Initial Count Register,设置初始计数值。 - - LVT Timer(0x320): Local Vector Table Timer,设置中断向量号和触发模式。 + - TDCR(0x3E0): Divide Configuration Register,设置分频系数。 + - TICR(0x380): Initial Count Register,设置初始计数值。 + - LVT Timer(0x320): Local Vector Table Timer,设置中断向量号和触发模式。 -- 禁用 LVT LINT0, LVT LINT1,LVT PCINT,向对应寄存器写入 Mask 位。 +- 禁用 LVT LINT0, LVT LINT1,LVT PCINT,向对应寄存器写入 Mask 位。 -- 设置错误中断 LVT Error 到对应的中断向量号。 +- 设置错误中断 LVT Error 到对应的中断向量号。 -- 连续写入两次 0 以清除错误状态寄存器。 +- 连续写入两次 0 以清除错误状态寄存器。 -- 向 EOI 寄存器写入 0 以确认任何挂起的中断。 +- 向 EOI 寄存器写入 0 以确认任何挂起的中断。 -- 设置 ICR 寄存器: +- 设置 ICR 寄存器: - - Destination Shorthand(bit 18-19): 设置为 2,始终将中断发送给所有 APIC - - Delivery Mode(bit 8-10): 设置为 5,INIT De-assert 模式所需 - - Level(bit 14): 设置为 0,INIT De-assert 所需 - - Trigger Mode(bit 15): 设置为 1,INIT De-assert 所需 + - Destination Shorthand(bit 18-19): 设置为 2,始终将中断发送给所有 APIC + - Delivery Mode(bit 8-10): 设置为 5,INIT De-assert 模式所需 + - Level(bit 14): 设置为 0,INIT De-assert 所需 + - Trigger Mode(bit 15): 设置为 1,INIT De-assert 所需 设置完成后等待 Delivery Status(bit 12) 为 0。 -- 设置 TPR 寄存器为 0,允许接收中断。 +- 设置 TPR 寄存器为 0,允许接收中断。 以上过程的代码示例会在实验任务文档中进行详细描述,具体细节和设置原因涉及对称多处理 SMP 等内容,不做理解要求,如有兴趣可以自行查阅参考资料了解。 @@ -67,9 +67,9 @@ APIC 的初始化过程基本包括以下几个步骤: ## 参考资料 -- [APIC - OSDev](https://wiki.osdev.org/APIC) -- [/arch/x86/kernel/apic/apic.c - Linux](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/apic/apic.c?h=v6.7#n1525) -- [Symmetric Multiprocessing - OSDev](https://wiki.osdev.org/Symmetric_Multiprocessing) -- [APIC Timer - OSDev](https://wiki.osdev.org/APIC_timer) -- [Multiprocessing Support for Hobby OSes Explained](http://www.osdever.net/tutorials/view/multiprocessing-support-for-hobby-oses-explained) -- [apic crate - theseus-os](https://www.theseus-os.com/Theseus/doc/apic/) +- [APIC - OSDev](https://wiki.osdev.org/APIC) +- [/arch/x86/kernel/apic/apic.c - Linux](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/apic/apic.c?h=v6.7#n1525) +- [Symmetric Multiprocessing - OSDev](https://wiki.osdev.org/Symmetric_Multiprocessing) +- [APIC Timer - OSDev](https://wiki.osdev.org/APIC_timer) +- [Multiprocessing Support for Hobby OSes Explained](http://www.osdever.net/tutorials/view/multiprocessing-support-for-hobby-oses-explained) +- [apic crate - theseus-os](https://www.theseus-os.com/Theseus/doc/apic/) diff --git a/docs/wiki/ata.md b/docs/wiki/ata.md index 0fb3d35..a94e635 100644 --- a/docs/wiki/ata.md +++ b/docs/wiki/ata.md @@ -10,8 +10,8 @@ ATA 接口经历了多个版本的发展,包括 ATA、ATAPI(用于附加设 由上介绍,ATA 硬盘使用 IDE 接口,分为 PATA 和 SATA 两种类型。 -- **PATA 硬盘**(Parallel ATA):即并行 ATA 硬盘接口规范,有着非常悠久的迭代历史,但已经不常见。 -- **SATA 硬盘**(Serial ATA):即串行 ATA 硬盘接口规范,为常见类型,如今我们常说的外存设备 HDD 或 SSD 多数为 SATA 接口。 +- **PATA 硬盘**(Parallel ATA):即并行 ATA 硬盘接口规范,有着非常悠久的迭代历史,但已经不常见。 +- **SATA 硬盘**(Serial ATA):即串行 ATA 硬盘接口规范,为常见类型,如今我们常说的外存设备 HDD 或 SSD 多数为 SATA 接口。 PATA 之所以被 SATA 取代,其中一个重要原因是 SATA 接口的传输速度更快。到目前为止,ATA-7 是 ATA 接口的最后一个版本,也叫 ATA133 。 ATA133 最大支持 133 MB/s 数据传输速度。但 SATA 接口的第一代就已经达到了 150 MB/s 的传输速度,第二代则达到了 300 MB/s,第三代则达到了 600 MB/s。从数据上可以直观发现,SATA 硬盘的最大传输速度远远超过了 PATA 硬盘。 @@ -23,8 +23,8 @@ PATA 之所以被 SATA 取代,其中一个重要原因是 SATA 接口的传输 为什么 PATA 接口会比 SATA 接口慢呢?更进一步,为什么现在流行的高速接口都是串行接口呢?直观上感觉,相同情况下 ATA 的**多通道**应该比 SATA 的**单通道**应该更快才对。这里我们需要了解一些比特数据传输与速率限制的知识。相较 PATA 的多通道,SATA 的单数据通道速度频率限制: -- 由于多通道并行,PATA 必须在数据线中一次传输 16 个信号,如果信号没有及时到达,就会产生错误传输结果。因此,比特流传输的速度必须**减缓**以纠正错误。 -- 而 SATA 一次只传输一个比特的数据,此时比特流的传递速度要快得多。 +- 由于多通道并行,PATA 必须在数据线中一次传输 16 个信号,如果信号没有及时到达,就会产生错误传输结果。因此,比特流传输的速度必须**减缓**以纠正错误。 +- 而 SATA 一次只传输一个比特的数据,此时比特流的传递速度要快得多。 因此,SATA 传输线的传输速度比 PATA 要快了近 30 倍。此外,SATA 另一个进步在于它的数据连线,它的体积更小,散热也更好,与硬盘的连接相当方便。与 PATA 相比,SATA 的功耗更低,同时配备的 CRC 技术让数据传输也更为安全。 @@ -76,7 +76,7 @@ PIO (Programmed Input/Output) 是过 CPU 执行 I/O 端口指令来进行数据 ## 参考资料 -- [ATA PIO - OSDev](https://wiki.osdev.org/ATA_PIO) -- [ATA - Wikipedia](https://en.wikipedia.org/wiki/Parallel_ATA) -- [百度百科 - ATA 硬盘](https://baike.baidu.com/item/ATA%E7%A1%AC%E7%9B%98/10009258) -- [知乎 - ATA PIO 模式](https://zhuanlan.zhihu.com/p/653413684) +- [ATA PIO - OSDev](https://wiki.osdev.org/ATA_PIO) +- [ATA - Wikipedia](https://en.wikipedia.org/wiki/Parallel_ATA) +- [百度百科 - ATA 硬盘](https://baike.baidu.com/item/ATA%E7%A1%AC%E7%9B%98/10009258) +- [知乎 - ATA PIO 模式](https://zhuanlan.zhihu.com/p/653413684) diff --git a/docs/wiki/debug.md b/docs/wiki/debug.md index a6ad6da..5d92d9f 100644 --- a/docs/wiki/debug.md +++ b/docs/wiki/debug.md @@ -111,8 +111,8 @@ b ysos_kernel::init 以上是 GDB 的基本使用方法,更多的 GDB 使用方法请参考: -- [官方文档](https://sourceware.org/gdb/current/onlinedocs/gdb/) -- [知乎:GDB 调试入门指南](https://zhuanlan.zhihu.com/p/74897601) +- [官方文档](https://sourceware.org/gdb/current/onlinedocs/gdb/) +- [知乎:GDB 调试入门指南](https://zhuanlan.zhihu.com/p/74897601) ## 命令行调试进阶:gef @@ -213,8 +213,8 @@ python ysos.py run -p debug -d 以上即是 VSCode + LLDB 的调试效果。可以看到,VSCode 提供了一个非常方便的调试界面,同时也提供了丰富的调试功能: -- 可以在左侧的 `VARIABLES` 中查看变量的值; -- 也可以在 `CALL STACK` 中查看函数调用栈。 -- 更多的调试功能请参考[官方文档](https://code.visualstudio.com/docs/editor/debugging)。 +- 可以在左侧的 `VARIABLES` 中查看变量的值; +- 也可以在 `CALL STACK` 中查看函数调用栈。 +- 更多的调试功能请参考[官方文档](https://code.visualstudio.com/docs/editor/debugging)。 以上即基本调试方法入门,欢迎大家探索属于自己的调试工具链。 diff --git a/docs/wiki/elf.md b/docs/wiki/elf.md index c64b71a..9130b1f 100644 --- a/docs/wiki/elf.md +++ b/docs/wiki/elf.md @@ -12,10 +12,10 @@ ELF 文件大体上由文件头和数据组成,它还可以加上额外的调 一般来说,ELF 有以下几个部分 -- ELF 文件头 -- Section header table,为 relocatable files 所必须,loadable files 可选,链接器需要 Section Table 进行链接 -- Program header table,为 loadable files 所必需,但 relocatable files 可选,Program header table 描述了所有可加载的 segments 和其他数据结构,这或许会是我们遇见最多的 -- 有文件头还得有内容,即 section 和 segment,这包括了各种可加载的数据,字符串表,符号表等等。每个 segment 里可以包含多个 sections。 +- ELF 文件头 +- Section header table,为 relocatable files 所必须,loadable files 可选,链接器需要 Section Table 进行链接 +- Program header table,为 loadable files 所必需,但 relocatable files 可选,Program header table 描述了所有可加载的 segments 和其他数据结构,这或许会是我们遇见最多的 +- 有文件头还得有内容,即 section 和 segment,这包括了各种可加载的数据,字符串表,符号表等等。每个 segment 里可以包含多个 sections。 ### ELF Header @@ -57,7 +57,7 @@ typedef __u64 Elf64_Xword; typedef __s64 Elf64_Sxword; ``` -- `e_ident` ,即 ELF identification,描述了“这是一个 ELF 文件” +- `e_ident` ,即 ELF identification,描述了“这是一个 ELF 文件” ```shell ➜ xiao hexdump -C ./this_is_an_elf_file | head -1 @@ -137,37 +137,37 @@ typedef __s64 Elf64_Sxword; } ``` -- `e_type` 描述 ELF 的类型,包括: +- `e_type` 描述 ELF 的类型,包括: - - `ET_NONE` 没有类型也是类型 - - `ET_REL` Relocatable file - - `ET_EXEC` Executable file - - `ET_DYN` Shared object file - - `ET_CORE` Core file, Coredump 也是 ELF 类型 + - `ET_NONE` 没有类型也是类型 + - `ET_REL` Relocatable file + - `ET_EXEC` Executable file + - `ET_DYN` Shared object file + - `ET_CORE` Core file, Coredump 也是 ELF 类型 -- `e_machine` 描述目标平台 +- `e_machine` 描述目标平台 -- `e_version` 描述版本 +- `e_version` 描述版本 -- `e_entry` 储存 ELF 文件的入口虚拟地址 +- `e_entry` 储存 ELF 文件的入口虚拟地址 -- `e_phoff` 储存 ELF Program header 的 offset,也就是说,Program header 储存在距离文件开头 `e_phoff`的位置 +- `e_phoff` 储存 ELF Program header 的 offset,也就是说,Program header 储存在距离文件开头 `e_phoff`的位置 -- `e_shoff` 储存 ELF Section header 的 offset +- `e_shoff` 储存 ELF Section header 的 offset -- `e_flags` 处理器特定的 flags +- `e_flags` 处理器特定的 flags -- `e_ehsize` ELF 文件头的大小 +- `e_ehsize` ELF 文件头的大小 -- `e_phentsize` ELF Program header entry 的大小 +- `e_phentsize` ELF Program header entry 的大小 -- `e_phnum` ELF Program header 的数量 +- `e_phnum` ELF Program header 的数量 -- `e_shentsize` 类似 `e_phentsize`但是是 Section +- `e_shentsize` 类似 `e_phentsize`但是是 Section -- `e_shnum` 同上类推 +- `e_shnum` 同上类推 -- `e_shstrndx` Section 中字符串表的 index +- `e_shstrndx` Section 中字符串表的 index ### Section Header @@ -189,9 +189,9 @@ typedef struct elf64_shdr { ``` -- `sh_flags` 描述了 Section 的一些属性,包括 `SHF_WRITE`,`SHF_ALLOC`,`SHF_EXECINSTR` 等等 +- `sh_flags` 描述了 Section 的一些属性,包括 `SHF_WRITE`,`SHF_ALLOC`,`SHF_EXECINSTR` 等等 -- `sh_type`描述了 Section 的类型,包括了储存 dynamic linking table 的 `SHT_DYNAMIC` ,存放 linker symbol table 的 `SHT_SYMTAB`,由程序定义的 `SHT_PROGBITS`等等 +- `sh_type`描述了 Section 的类型,包括了储存 dynamic linking table 的 `SHT_DYNAMIC` ,存放 linker symbol table 的 `SHT_SYMTAB`,由程序定义的 `SHT_PROGBITS`等等 使用 `readelf -S`可以观察程序的 section headers @@ -228,9 +228,9 @@ typedef struct elf64_phdr { ``` -- `p_type`表示 segment 的类型,包括有 `PT_LOAD`,`PT_DYNAMIC`,`PT_INTERP`等等。 +- `p_type`表示 segment 的类型,包括有 `PT_LOAD`,`PT_DYNAMIC`,`PT_INTERP`等等。 -- `p_flags` 包括有 `PF_X`,`PF_W`,`PF_R`等等,通过不同的 bit 表达不同的信息,可以相互组合。这决定了 segment 映射时的权限。 +- `p_flags` 包括有 `PF_X`,`PF_W`,`PF_R`等等,通过不同的 bit 表达不同的信息,可以相互组合。这决定了 segment 映射时的权限。 ```log root@da070736a297:/# readelf -l /bin/sh diff --git a/docs/wiki/fat.md b/docs/wiki/fat.md index 1803365..53bda35 100644 --- a/docs/wiki/fat.md +++ b/docs/wiki/fat.md @@ -38,12 +38,12 @@ root/ 下图表示了一个文件系统磁盘中可能的样子: -- `#0` 标号的内存空间对应着系统的根目录 `/` 节点,其内部有两个有效的文件信息,分别是 `system` 和 `home`,均为目录类别。 -- `system` 首文件块指向 `#8`,并且 `#8` 为其最后一个文件块,因此 `#8` 实际上就对应着 `/system/` 节点,其中存储了 `kernel.elf` 一个文件的信息。 -- 对于 `home`,首文件块指向 `#14`,其下一个文件块为 `#16`,并且是最后一个文件块,因此 `#14`, `#16` 共同构成 `/home/` 节点,在这个节点中存储了两个有效文件信息,分别是 `boo.baz` 和 `bar`,分别是文件和目录。 -- `kernel.elf` 首文件块为 `#4`,`#4` 下一个文件块为 `#6`,`#6` 为最后一个文件块,因此 `kernel.elf` 对应的文件数据为 `#4`, `#6` 上的数据。 -- `boo.baz` 首文件块为 `#10`,后续文件块依次为 `#12`、`#5`。因此 `#10`, `#12`, `#5` 共同构成 `boo.baz` 文件的数据 -- `bar` 首文件块指向 `#18`,后续文件块为 `#11`,因此 `#18`, `#11` 共同构成了目录树中的 `/home/bar/` 节点的子树信息 +- `#0` 标号的内存空间对应着系统的根目录 `/` 节点,其内部有两个有效的文件信息,分别是 `system` 和 `home`,均为目录类别。 +- `system` 首文件块指向 `#8`,并且 `#8` 为其最后一个文件块,因此 `#8` 实际上就对应着 `/system/` 节点,其中存储了 `kernel.elf` 一个文件的信息。 +- 对于 `home`,首文件块指向 `#14`,其下一个文件块为 `#16`,并且是最后一个文件块,因此 `#14`, `#16` 共同构成 `/home/` 节点,在这个节点中存储了两个有效文件信息,分别是 `boo.baz` 和 `bar`,分别是文件和目录。 +- `kernel.elf` 首文件块为 `#4`,`#4` 下一个文件块为 `#6`,`#6` 为最后一个文件块,因此 `kernel.elf` 对应的文件数据为 `#4`, `#6` 上的数据。 +- `boo.baz` 首文件块为 `#10`,后续文件块依次为 `#12`、`#5`。因此 `#10`, `#12`, `#5` 共同构成 `boo.baz` 文件的数据 +- `bar` 首文件块指向 `#18`,后续文件块为 `#11`,因此 `#18`, `#11` 共同构成了目录树中的 `/home/bar/` 节点的子树信息 directory-structure @@ -55,10 +55,10 @@ root/ 现在,假设我们需要访问 `/system/kernel.elf` 文件,那么就需要: -- 首先从树的根部开始,找到类型为目录,名称为 `system` 的信息。 -- 从信息中读取出目录数据的第一个块 `#8`,并根据链表中的信息读入整个目录,由于这里只有一个块,所以就读入 `#8`。 -- 在目录的数据中找到类型为文件,名称为 `kernel.elf` 的信息。 -- 从信息中读出文件数据的第一个块 `#4`,并根据链表读入 `#6`,得到整个文件。 +- 首先从树的根部开始,找到类型为目录,名称为 `system` 的信息。 +- 从信息中读取出目录数据的第一个块 `#8`,并根据链表中的信息读入整个目录,由于这里只有一个块,所以就读入 `#8`。 +- 在目录的数据中找到类型为文件,名称为 `kernel.elf` 的信息。 +- 从信息中读出文件数据的第一个块 `#4`,并根据链表读入 `#6`,得到整个文件。 为了提高文件读写的速度,系统一般会维护一个打开的文件表 (Open-file table) 用于维护所有打开的文件的信息,所以一般来说,当打开一个文件的时候,除了会将它的全部数据就被读取到内存中,还会将其信息添加到打开文件表中,后续所有的增删改都只会对内存中的数据起作用,直到文件关闭时才再次写回磁盘。这样就避免了每次读写过程中对文件数据的定位以及读取。 @@ -90,9 +90,9 @@ root/ 第一章大体可以略过,其中较为重要的概念如下: -- 簇 (cluster): 一组逻辑连续的扇区。卷内的每个簇都可以通过一个簇号 N 指代 (referred to)。簇是文件分配的最小单元 (A unit of allocation),给任何一个文件所分配的全部空间的大小 _(All allocation for a file)_ 必须是簇的整数倍。 -- 分区 (partition): 在一个卷内的一系列扇区 -- 卷 (volume): 一个逻辑上的连续的扇区地址空间 +- 簇 (cluster): 一组逻辑连续的扇区。卷内的每个簇都可以通过一个簇号 N 指代 (referred to)。簇是文件分配的最小单元 (A unit of allocation),给任何一个文件所分配的全部空间的大小 _(All allocation for a file)_ 必须是簇的整数倍。 +- 分区 (partition): 在一个卷内的一系列扇区 +- 卷 (volume): 一个逻辑上的连续的扇区地址空间 ### FAT 卷结构 @@ -138,8 +138,8 @@ if(CountofClusters < 4085) { 也就是说,FAT 的类型只与簇的数量有关,而与磁盘的大小、扇区数量以及 BPB 中的 BS_FilSysType 域都无关,其中 -- FAT12 不能有超过 4084 个簇 -- FAT16 不能有超过 65524 个簇,也不能少于 4085 个簇 +- FAT12 不能有超过 4084 个簇 +- FAT16 不能有超过 65524 个簇,也不能少于 4085 个簇 ### FAT @@ -151,9 +151,9 @@ if(CountofClusters < 4085) { 对于不同的 FAT 格式,FAT 表中每个条目 (entry) 的大小不同: -- 对于 FAT12 而言,每个条目长 12 位 -- 对于 FAT16 而言,每个条目长 16 位 -- 对于 FAT32 而言,每个条目长 32 位 +- 对于 FAT12 而言,每个条目长 12 位 +- 对于 FAT16 而言,每个条目长 16 位 +- 对于 FAT32 而言,每个条目长 32 位 这时候就发现了,原来 FAT 后面的数字就是指代的 FAT 表中每个条目的长度,因此对于我们需要实现的 FAT16,每个条目长 16 位 @@ -171,9 +171,9 @@ FAT 表中第一个保留的条目 `FAT [0]` 包含了 `BPB_Media` 域中的内 例如,如果 `BPB_Media` 的值为 `0xF8`,那么 -- 对于 FAT12,值为 `0xFF8` -- 对于 FAT16,值为 `0xFFF8` -- 对于 FAT32,值为 `0xFFFFFF8` +- 对于 FAT12,值为 `0xFF8` +- 对于 FAT16,值为 `0xFFF8` +- 对于 FAT32,值为 `0xFFFFFF8` 第二个保留的条目 `FAT [1]` 在格式化时会被格式化工具赋一个 EOC 值,具体用处不明。 @@ -197,12 +197,12 @@ FAT 表中第一个保留的条目 `FAT [0]` 包含了 `BPB_Media` 域中的内 除此之外,短名称域还遵循以下规则: -- 若首字节为 `0xE5` 则代表当前条目为空 -- 若首字节为 `0x00` 则同样代表当前条目为空,并且还代表当前条目之后的所有条目都为空 -- 首字节不能为空格,也就是说文件名不能以空格开头 -- 目录中不能出现名称相同的两个条目 -- 不能出现小写字母 -- 不能出现ASCII 值小于 `0x20` 的字符(控制字符)以及 `0x22`、`0x2A`、`0x2B`、`0x2C`、`0x2E`、`0x2F`、`0x3A`、`0x3B`、`0x3C`、`0x3D`、`0x3E`、`0x3F`、`0x5B`、`0x5C`、`0x5D`、`0x7C`(部分特殊符号) +- 若首字节为 `0xE5` 则代表当前条目为空 +- 若首字节为 `0x00` 则同样代表当前条目为空,并且还代表当前条目之后的所有条目都为空 +- 首字节不能为空格,也就是说文件名不能以空格开头 +- 目录中不能出现名称相同的两个条目 +- 不能出现小写字母 +- 不能出现ASCII 值小于 `0x20` 的字符(控制字符)以及 `0x22`、`0x2A`、`0x2B`、`0x2C`、`0x2E`、`0x2F`、`0x3A`、`0x3B`、`0x3C`、`0x3D`、`0x3E`、`0x3F`、`0x5B`、`0x5C`、`0x5D`、`0x7C`(部分特殊符号) 然而,如果文件名称很长的话,短名称就显得不太够用了,所以 FAT 还额外提出了长名称解决方案,并且称存储短名称的条目为 `SFNEntry`,存储长名称的条目为 `LFNEntry`。 @@ -210,11 +210,11 @@ FAT 表中第一个保留的条目 `FAT [0]` 包含了 `BPB_Media` 域中的内 在目录中,一个文件对应的长名称条目和短名称条目连续存储,其中地址从低到高依次存储: -- 第 N 个长名称条目 -- 第 N-1 个长名称条目 -- … -- 第 1 个长名称条目 -- 短名称条目 +- 第 N 个长名称条目 +- 第 N-1 个长名称条目 +- … +- 第 1 个长名称条目 +- 短名称条目 但是目前短名称足够使用了,所以不妨暂时跳过长名称的进一步介绍和实现。 @@ -230,20 +230,20 @@ FAT 表中第一个保留的条目 `FAT [0]` 包含了 `BPB_Media` 域中的内 其中文件的日期包括三个部分: -- `[4:0]`:日(从 1 至 31) -- `[8:5]`:月(从 1 至 12) -- `[15:9]`:从 1980 年起的年份偏移(从 0 至 127) +- `[4:0]`:日(从 1 至 31) +- `[8:5]`:月(从 1 至 12) +- `[15:9]`:从 1980 年起的年份偏移(从 0 至 127) 文件的时间同样也包括三个部分: -- `[4:0]`:从零起经过的 2s 间隔数(从 0 至 29) -- `[10:5]`:分(从 0 至 59) -- `[15:11]`:时(从 0 至 23) +- `[4:0]`:从零起经过的 2s 间隔数(从 0 至 29) +- `[10:5]`:分(从 0 至 59) +- `[15:11]`:时(从 0 至 23) 可以看到,秒的精度为 2s,这也就引出了 CrtTimeTenth 这个域,其精度为 10ms,范围从 0 至 199,正好填补了 2s 之间的空缺,使得精度提高到 10ms。 ## 参考资料 -- [FAT - OSDev](https://osdev.org/FAT) -- [File Allocation Table - Wikipedia](https://en.wikipedia.org/wiki/File_Allocation_Table) -- [南京大学操作系统课程中关于 FAT 和 UNIX 文件系统知识的讲解 - Bilibili](https://www.bilibili.com/video/BV1oZ4y1t7ce) +- [FAT - OSDev](https://osdev.org/FAT) +- [File Allocation Table - Wikipedia](https://en.wikipedia.org/wiki/File_Allocation_Table) +- [南京大学操作系统课程中关于 FAT 和 UNIX 文件系统知识的讲解 - Bilibili](https://www.bilibili.com/video/BV1oZ4y1t7ce) diff --git a/docs/wiki/fs.md b/docs/wiki/fs.md index 6a7fcac..0366b0b 100644 --- a/docs/wiki/fs.md +++ b/docs/wiki/fs.md @@ -14,8 +14,8 @@ 要想深刻理解文件系统,首先需要了解文件与持久存储。由于计算机内存中的数据在掉电后会丢失,应用程序需要将数据保存在持久存储设备上。为了满足这个需求,计算机操作系统一般会有两种处理方法: -- 应用程序直接操作持久存储设备,自己维护数据的存储和读写。 -- 应用程序通过操作系统提供的文件系统接口,由操作系统负责维护数据的存储和读写。 +- 应用程序直接操作持久存储设备,自己维护数据的存储和读写。 +- 应用程序通过操作系统提供的文件系统接口,由操作系统负责维护数据的存储和读写。 对于前者,由之前的学习可以知道:计算机中应用程序若要和设备直接交互,需要操作系统让渡部分设备管理权限,无形中会引入安全隐患(如侧信道攻击等)。此外,不同的设备有不同的操作模式,应用程序需要针对不同的设备编写不同的代码,增加了开发难度的同时,还降低了应用的可移植性。 @@ -53,9 +53,9 @@ 在本实验和[rCore](https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter6/2fs-implementation.html)的设计中,都采用了这样的思路,具体可以体现在: -- `BlockDevice`:文件系统与底层设备驱动之间通过抽象层 `BlockDevice` 来连接,避免了与设备驱动、分区方式的绑定。 -- `alloc` crate:通过 Rust 提供的 `alloc crate` 来隔离了语言层面的内存管理,避免了直接调用内存管理的内核函数,同时提供了在 `std` 环境下进行测试的能力。 -- `trait` 和 `dyn`:通过对文件系统的功能、相关数据结构进行抽象封装,从而使得创建虚拟文件系统和支持更多不同的文件系统成为可能。 +- `BlockDevice`:文件系统与底层设备驱动之间通过抽象层 `BlockDevice` 来连接,避免了与设备驱动、分区方式的绑定。 +- `alloc` crate:通过 Rust 提供的 `alloc crate` 来隔离了语言层面的内存管理,避免了直接调用内存管理的内核函数,同时提供了在 `std` 环境下进行测试的能力。 +- `trait` 和 `dyn`:通过对文件系统的功能、相关数据结构进行抽象封装,从而使得创建虚拟文件系统和支持更多不同的文件系统成为可能。 ### 实现方法 @@ -93,11 +93,11 @@ FAT 的实现很简单:它将文件系统的元信息和文件数据都存储 ## 参考资料 -- [File Systems - OSDev](https://wiki.osdev.org/File_Systems) -- [rCore - fs implementation](https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter6/index.html#) -- [FAT 和 UNIX 文件系统](https://jyywiki.cn/OS/2022/slides/27.slides.html#/) -- [Xv6 文件系统实现](https://jyywiki.cn/OS/2022/slides/29.slides.html#/) -- [你管这破玩意叫文件系统 - 微信科普公众号](https://mp.weixin.qq.com/s/q6OjwCXSk05TvX_BIu1M0g) -- [OSDI '22 Best Paper: XRP](https://www.usenix.org/conference/osdi22/presentation/zhong) -- [SPDKL: The Storage Performance Development Kit](https://spdk.io/doc/about.html) -- [存储系统-从没入门到刚入门](https://www.yuque.com/wwyf/blog/dhoobh) +- [File Systems - OSDev](https://wiki.osdev.org/File_Systems) +- [rCore - fs implementation](https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter6/index.html#) +- [FAT 和 UNIX 文件系统](https://jyywiki.cn/OS/2022/slides/27.slides.html#/) +- [Xv6 文件系统实现](https://jyywiki.cn/OS/2022/slides/29.slides.html#/) +- [你管这破玩意叫文件系统 - 微信科普公众号](https://mp.weixin.qq.com/s/q6OjwCXSk05TvX_BIu1M0g) +- [OSDI '22 Best Paper: XRP](https://www.usenix.org/conference/osdi22/presentation/zhong) +- [SPDKL: The Storage Performance Development Kit](https://spdk.io/doc/about.html) +- [存储系统-从没入门到刚入门](https://www.yuque.com/wwyf/blog/dhoobh) diff --git a/docs/wiki/linux.md b/docs/wiki/linux.md index eeb8574..8ae3de9 100644 --- a/docs/wiki/linux.md +++ b/docs/wiki/linux.md @@ -88,13 +88,13 @@ wsl --install -d Ubuntu ```bash $ rustc --version -rustc 1.82.0 (f6e511eec 2024-10-15) +rustc 1.83.0 (90b35a623 2024-11-26) $ rustc +nightly --version -rustc 1.84.0-nightly (3ed6e3cc6 2024-10-17) +rustc 1.85.0-nightly (9c707a8b7 2024-12-07) $ qemu-system-x86_64 --version -QEMU emulator version 8.2.2 (Debian 1:8.2.2+ds-0ubuntu1.2) +QEMU emulator version 8.2.2 (Debian 1:8.2.2+ds-0ubuntu1.4) $ gcc --version gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0 @@ -107,10 +107,10 @@ GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git ```bash $ rustc --version -rustc 1.82.0 (f6e511eec 2024-10-15) +rustc 1.83.0 (90b35a623 2024-11-26) $ rustc +nightly --version -rustc 1.84.0-nightly (3ed6e3cc6 2024-10-17) +rustc 1.85.0-nightly (9c707a8b7 2024-12-07) $ qemu-system-x86_64 --version QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.15) diff --git a/docs/wiki/paging.md b/docs/wiki/paging.md index c37e204..f977494 100644 --- a/docs/wiki/paging.md +++ b/docs/wiki/paging.md @@ -48,9 +48,9 @@ 页表中的每一个表项都是一个 64 位的数据,能够描述一个页表项的属性,及一些权限管理: -- Readable:是否可读,表示是否允许从这个页读取数据。 -- Writable:是否可写,表示是否允许向这个页写入数据。 -- Executable:是否可执行,表示是否允许执行这个页上的指令。 +- Readable:是否可读,表示是否允许从这个页读取数据。 +- Writable:是否可写,表示是否允许向这个页写入数据。 +- Executable:是否可执行,表示是否允许执行这个页上的指令。 这三个主要权限一般被简写为 R/W/X,这部分内容将在处理 ELF 文件时用到。 @@ -66,9 +66,9 @@ 在 x64 架构中,有一些特殊的寄存器与页表相关,且在本实验中会用到,它们分别是: -- CR0:控制寄存器,存储了一些控制系统运行状态的标志位,包括根页表的写保护位。 -- CR2:页错误地址寄存器,存储了最近一次页错误的**虚拟地址**。 -- CR3:页表根地址寄存器,存储了页表的**物理地址**,也就是 PML4 的地址。 +- CR0:控制寄存器,存储了一些控制系统运行状态的标志位,包括根页表的写保护位。 +- CR2:页错误地址寄存器,存储了最近一次页错误的**虚拟地址**。 +- CR3:页表根地址寄存器,存储了页表的**物理地址**,也就是 PML4 的地址。 ## 参考资料 diff --git a/docs/wiki/rust.md b/docs/wiki/rust.md index fd9163e..3c97be1 100644 --- a/docs/wiki/rust.md +++ b/docs/wiki/rust.md @@ -10,30 +10,30 @@ Rust 语言的基础语法可以参考 [Rust圣经](https://course.rs/) 或者 [ 其他可参考的学习资料: -- [Rust 之旅(交互式课程)](https://tourofrust.com/00_zh-cn.html) -- [Rust by Example](https://doc.rust-lang.org/rust-by-example/) -- [Rust Cookbook](https://rust-lang-nursery.github.io/rust-cookbook/) -- [清华的 Rust 课程](https://lab.cs.tsinghua.edu.cn/rust/) +- [Rust 之旅(交互式课程)](https://tourofrust.com/00_zh-cn.html) +- [Rust by Example](https://doc.rust-lang.org/rust-by-example/) +- [Rust Cookbook](https://rust-lang-nursery.github.io/rust-cookbook/) +- [清华的 Rust 课程](https://lab.cs.tsinghua.edu.cn/rust/) 值得注意的是,本实验内容并不要求你对 Rust 语言有深入的了解,只需要你能够 **理解并使用** Rust 语言的以下内容: -- **基本语法** +- **基本语法** 变量绑定、常量、表达式、基本类型、条件语句、模式匹配、函数 -- **所有权与结构化数据** +- **所有权与结构化数据** 所有权、移动语义、借用与可变引用、结构体、元组结构体、单位元结构体、枚举 -- **标准库** +- **标准库** `String`、`Vec`、`Result`、`Option`、错误处理、单元测试 -- **泛型、特型与生命周期** +- **泛型、特型与生命周期** 泛型、特型、标准库提供的常用特性、生命周期入门 -- **项目管理与常用库** +- **项目管理与常用库** Cargo 项目结构、命名规范、智能指针 diff --git a/docs/wiki/structures.md b/docs/wiki/structures.md index 33a1190..6b45dbd 100644 --- a/docs/wiki/structures.md +++ b/docs/wiki/structures.md @@ -28,9 +28,9 @@ 在 64 位长模式下,TSS 的结构与 32 位不同,它并不直接与任务切换挂钩,但是它仍然被用于存储特权级栈和中断栈: -- 特权级栈:最多三个,当权限级别从较低权限级别更改为较高权限级别时使用的栈指针。 -- 中断栈:最多七个,当中断发生时,如果定义了对应的中断栈,则使用对应栈指针。它可以用于存储不同**大小和用途**的中断栈,以处理不同的中断。 -- I/O 映射基址:包含从 TSS 底部到 I/O 权限位图的 16 位偏移量,在我们的实验中不会使用到。 +- 特权级栈:最多三个,当权限级别从较低权限级别更改为较高权限级别时使用的栈指针。 +- 中断栈:最多七个,当中断发生时,如果定义了对应的中断栈,则使用对应栈指针。它可以用于存储不同**大小和用途**的中断栈,以处理不同的中断。 +- I/O 映射基址:包含从 TSS 底部到 I/O 权限位图的 16 位偏移量,在我们的实验中不会使用到。 ## 中断描述符表 @@ -50,9 +50,9 @@ 门类型(Gate Type)字段指定了中断门的类型,包括: -- 中断门(Interrupt Gate):用于处理中断,中断处理程序会被调用,中断返回后,CPU 会恢复中断前的执行状态。 -- 陷阱门(Trap Gate):用于处理陷阱,陷阱处理程序会被调用,不同之处在于,对于中断门,中断在进入时自动禁用,并在 IRET 时重新启用,而陷阱门则不会主动禁用中断。 -- 任务门(Task Gate):曾用于任务切换,它在 x86-64 上已被完全删除。 +- 中断门(Interrupt Gate):用于处理中断,中断处理程序会被调用,中断返回后,CPU 会恢复中断前的执行状态。 +- 陷阱门(Trap Gate):用于处理陷阱,陷阱处理程序会被调用,不同之处在于,对于中断门,中断在进入时自动禁用,并在 IRET 时重新启用,而陷阱门则不会主动禁用中断。 +- 任务门(Task Gate):曾用于任务切换,它在 x86-64 上已被完全删除。 ## 页表 diff --git a/docs/wiki/userspace.md b/docs/wiki/userspace.md index ab662d0..099d115 100644 --- a/docs/wiki/userspace.md +++ b/docs/wiki/userspace.md @@ -4,8 +4,8 @@ 从执行原理来说,不同进程的指令均运行在同一个处理器上,基于同一个处理器访问系统资源。但从资源管理、安全管理、崩溃预防的角度来看,操作系统需隔离敏感资源,限制不同进程的可执行的指令以及可访问的地址空间。因此,现代操作系统通常会将进程分为两种状态: -- 用户态(user mode):是指进程运行在低特权级别,只能执行受限指令,访问受限资源; -- 内核态(kernel mode):是指进程运行在高特权级别,可以执行特权指令,并且可以直接访问所有系统资源。 +- 用户态(user mode):是指进程运行在低特权级别,只能执行受限指令,访问受限资源; +- 内核态(kernel mode):是指进程运行在高特权级别,可以执行特权指令,并且可以直接访问所有系统资源。 处理器通常借助某个控制寄存器中的一个模式位(mode bit)来提供区分用户态、内核态。该寄存器描述了进程当前享有的特权。一个运行在内核态的进程可以执行指令集中的任何指令,并访问系统中的任何内存位置。 @@ -23,9 +23,9 @@ 在 x86_64 体系结构中,操作系统和应用程序运行在不同的特权级别(privilege levels)中,这些级别被称为 Rings。一般来说,较*低*的数字代表更*高*的特权级别: -- Ring 0:内核态,最高特权级别,可以执行所有指令,访问所有内存。 -- Ring 1/2:被设计用于设备驱动程序和其他系统服务,但在现代操作系统中不再使用。 -- Ring 3:用户态,最低特权级别,只能执行受限指令,需要访问受限资源时,必须通过系统调用等方式。 +- Ring 0:内核态,最高特权级别,可以执行所有指令,访问所有内存。 +- Ring 1/2:被设计用于设备驱动程序和其他系统服务,但在现代操作系统中不再使用。 +- Ring 3:用户态,最低特权级别,只能执行受限指令,需要访问受限资源时,必须通过系统调用等方式。 !!! question "为什么现代操作系统不使用 Ring 1/2?" @@ -68,9 +68,9 @@ C 语言标准库提供了一组函数和数据结构,供应用程序使用以 这些用户态库的设计与其提供的服务紧密相关: -- **跨平台兼容性**:用户态库的设计要求之一即屏蔽底层硬件的差异,向上提供统一的编程接口。换言之,用户态库的一致可以保持用户态程序跨平台兼容性,在更换平台时不需要修改应用程序的代码。 -- **性能与效率**:用户态库的设计需要考虑到性能和效率,尤其是对于系统调用等底层操作的封装,需要尽可能地减少额外的开销。[这篇文章](http://arkanis.de/weblog/2017-01-05-measurements-of-system-call-performance-and-overhead)测量了不同系统调用的性能和开销,并讨论了系统调用设计方法。 -- **功能完备性**:库中需要包含常用的系统调用封装和其他功能,以便开发者可以方便地进行应用程序开发,而不需要重复造轮子。 +- **跨平台兼容性**:用户态库的设计要求之一即屏蔽底层硬件的差异,向上提供统一的编程接口。换言之,用户态库的一致可以保持用户态程序跨平台兼容性,在更换平台时不需要修改应用程序的代码。 +- **性能与效率**:用户态库的设计需要考虑到性能和效率,尤其是对于系统调用等底层操作的封装,需要尽可能地减少额外的开销。[这篇文章](http://arkanis.de/weblog/2017-01-05-measurements-of-system-call-performance-and-overhead)测量了不同系统调用的性能和开销,并讨论了系统调用设计方法。 +- **功能完备性**:库中需要包含常用的系统调用封装和其他功能,以便开发者可以方便地进行应用程序开发,而不需要重复造轮子。 ### 用户态库的服务 @@ -92,10 +92,10 @@ Rust 语言中的标准用户态库是 `std`,它提供了一系列的系统调 ## 参考资料 -- [Getting to Ring 3 - OSDev](https://wiki.osdev.org/Getting_to_Ring_3) -- [Security - OSDev](https://wiki.osdev.org/Security) -- [Paging - OSDev](https://wiki.osdev.org/Paging) -- [Protection ring - Wikipedia](https://en.wikipedia.org/wiki/Protection_ring) -- [Computer Systems: A Programmer's Perspective - CSAPP](http://www.csapp.cs.cmu.edu/3e/home.html) -- [Why do x86 CPUs only use 2 out of 4 rings?](https://superuser.com/questions/1063420/why-do-x86-cpus-only-use-2-out-of-4-rings) -- [printf 是怎么输出到控制台的呢?](https://www.zhihu.com/question/456916638/answer/3099313413) +- [Getting to Ring 3 - OSDev](https://wiki.osdev.org/Getting_to_Ring_3) +- [Security - OSDev](https://wiki.osdev.org/Security) +- [Paging - OSDev](https://wiki.osdev.org/Paging) +- [Protection ring - Wikipedia](https://en.wikipedia.org/wiki/Protection_ring) +- [Computer Systems: A Programmer's Perspective - CSAPP](http://www.csapp.cs.cmu.edu/3e/home.html) +- [Why do x86 CPUs only use 2 out of 4 rings?](https://superuser.com/questions/1063420/why-do-x86-cpus-only-use-2-out-of-4-rings) +- [printf 是怎么输出到控制台的呢?](https://www.zhihu.com/question/456916638/answer/3099313413) diff --git a/docs/wiki/windows.md b/docs/wiki/windows.md index 8be3bc3..16751e5 100644 --- a/docs/wiki/windows.md +++ b/docs/wiki/windows.md @@ -39,8 +39,8 @@ 如果想要变更安装路径,可以通过指定如下环境变量来实现: -- `RUSTUP_HOME`:rustup 的安装路径 -- `CARGO_HOME`:cargo 的安装路径 +- `RUSTUP_HOME`:rustup 的安装路径 +- `CARGO_HOME`:cargo 的安装路径 rustup 将会把 `CARO_HOME\bin` 添加到 `PATH` 环境变量中。 @@ -48,12 +48,12 @@ rustup 将会把 `CARO_HOME\bin` 添加到 `PATH` 环境变量中。 rust 提供了两种 windows 上的工具链:`msvc` 和 `gnu`,详细信息可以参考 [Windows - The rustup book](https://rust-lang.github.io/rustup/installation/windows.html)。 -- `msvc`:使用 Visual Studio 的 C++ 编译器,需要安装 Visual Studio。 +- `msvc`:使用 Visual Studio 的 C++ 编译器,需要安装 Visual Studio。 在安装 Visual Studio 时,需要选择如下组件: - - `MSVC v143 - VS 2022 C++ x64/x86 build tools (latest)` - - `Windows 11 SDK` + - `MSVC v143 - VS 2022 C++ x64/x86 build tools (latest)` + - `Windows 11 SDK` `msvc` 工具链可以提供更好的 Windows 应用兼容性,也是 Windows 上开发 rust 应用推荐的工具链。 @@ -61,7 +61,7 @@ rust 提供了两种 windows 上的工具链:`msvc` 和 `gnu`,详细信息 此工具链本体占用约为 600MB。推荐本来就有 Visual Studio 的同学使用此工具链。 -- `gnu`:使用 GNU 工具链,需要安装 MinGW,会随工具链一起自动安装。 +- `gnu`:使用 GNU 工具链,需要安装 MinGW,会随工具链一起自动安装。 此工具链总体占用约 1GB。推荐未安装 Visual Studio 的同学使用此工具链。 diff --git a/src/0x01/Cargo.toml b/src/0x01/Cargo.toml index d550954..68b00ab 100644 --- a/src/0x01/Cargo.toml +++ b/src/0x01/Cargo.toml @@ -13,3 +13,21 @@ debug = true [profile.release-with-debug.package."*"] debug = false + +[workspace.dependencies] +paste = "1.0" +spin = "0.9" +x86 = "0.52" +x86_64 = "0.15" +log = "0.4" +bitflags = "2.3" +libm = "0.2" +xmas-elf = "0.9" +arrayvec = { version = "0.7", default-features = false } +uefi = { version = "0.33", default-features = false } +lazy_static = { version = "1.4", features = ["spin_no_std"] } + +# Local dependencies + +elf = { path = "pkg/elf", package = "ysos_elf" } +boot = { path = "pkg/boot", default-features = false, package = "ysos_boot" } diff --git a/src/0x01/pkg/boot/Cargo.toml b/src/0x01/pkg/boot/Cargo.toml index 6858eed..a9309b8 100644 --- a/src/0x01/pkg/boot/Cargo.toml +++ b/src/0x01/pkg/boot/Cargo.toml @@ -5,12 +5,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -arrayvec = { version = "0.7", default-features = false } -uefi = { version = "0.33", default-features = false } -log = "0.4" -x86_64 = "0.15" -xmas-elf = "0.9" -elf = { package = "ysos_elf", path = "../elf" } +arrayvec = { workspace = true } +uefi = { workspace = true } +log = { workspace = true } +x86_64 = { workspace = true } +xmas-elf = { workspace = true } +elf = { workspace = true } [features] boot = ["uefi/alloc", "uefi/logger", "uefi/panic_handler", "uefi/global_allocator"] diff --git a/src/0x01/pkg/elf/Cargo.toml b/src/0x01/pkg/elf/Cargo.toml index f07c6c6..38125b9 100644 --- a/src/0x01/pkg/elf/Cargo.toml +++ b/src/0x01/pkg/elf/Cargo.toml @@ -5,6 +5,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4" -x86_64 = "0.15" -xmas-elf = "0.9" +log = { workspace = true } +x86_64 = { workspace = true } +xmas-elf = { workspace = true } diff --git a/src/0x01/pkg/kernel/Cargo.toml b/src/0x01/pkg/kernel/Cargo.toml index 89e94fb..76e8707 100644 --- a/src/0x01/pkg/kernel/Cargo.toml +++ b/src/0x01/pkg/kernel/Cargo.toml @@ -5,13 +5,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -boot = { package = "ysos_boot", path = "../boot", default-features = false } -lazy_static = { version = "1.4", features = ["spin_no_std"] } -uefi = { version = "0.33", default-features = false } -paste = "1.0" -spin = "0.9" -x86 = "0.52" -x86_64 = "0.15" -log = "0.4" -bitflags = "2.3" -libm = "0.2" +boot = { workspace = true } +uefi = { workspace = true } +paste = { workspace = true } +spin = { workspace = true } +x86 = { workspace = true } +x86_64 = { workspace = true } +log = { workspace = true } +bitflags = { workspace = true } +libm = { workspace = true } diff --git a/src/0x02/Cargo.toml b/src/0x02/Cargo.toml new file mode 100644 index 0000000..48075df --- /dev/null +++ b/src/0x02/Cargo.toml @@ -0,0 +1,37 @@ +[workspace] +resolver = "2" +members = [ + "pkg/elf", + "pkg/boot", + "pkg/kernel" +] +exclude = ["pkg/app/config", "pkg/app/.cargo"] + +[profile.release-with-debug] +inherits = "release" +debug = true + +[profile.release-with-debug.package."*"] +debug = false + +[workspace.dependencies] +paste = "1.0" +spin = "0.9" +x86 = "0.52" +x86_64 = "0.15" +log = "0.4" +spin = "0.9" +bit_field = "0.10" +bitflags = "2.3" +libm = "0.2" +xmas-elf = "0.9" +linked_list_allocator = "0.10" +arrayvec = { version = "0.7", default-features = false } +uefi = { version = "0.33", default-features = false } +lazy_static = { version = "1.4", features = ["spin_no_std"] } +crossbeam-queue = { version = "0.3", default-features = false, features = ["alloc"] } + +# Local dependencies + +elf = { path = "pkg/elf", package = "ysos_elf" } +boot = { path = "pkg/boot", default-features = false, package = "ysos_boot" } diff --git a/src/0x02/pkg/kernel/Cargo.toml b/src/0x02/pkg/kernel/Cargo.toml index 969ae81..c40b506 100644 --- a/src/0x02/pkg/kernel/Cargo.toml +++ b/src/0x02/pkg/kernel/Cargo.toml @@ -5,16 +5,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -boot = { package = "ysos_boot", path = "../boot", default-features = false } -lazy_static = { version = "1.4", features = ["spin_no_std"] } -uefi = { version = "0.33", default-features = false } -crossbeam-queue = { version = "0.3", default-features = false, features = ["alloc"] } -paste = "1.0" -spin = "0.9" -x86 = "0.52" -x86_64 = "0.15" -log = "0.4" -bitflags = "2.3" -bit_field = "0.10" -libm = "0.2" -linked_list_allocator = "0.10" +boot = { workspace = true } +lazy_static = { workspace = true } +uefi = { workspace = true } +crossbeam-queue = { workspace = true } +paste = { workspace = true } +spin = { workspace = true } +x86 = { workspace = true } +x86_64 = { workspace = true } +log = { workspace = true } +bitflags = { workspace = true } +bit_field = { workspace = true } +libm = { workspace = true } +linked_list_allocator = { workspace = true } diff --git a/src/0x03/pkg/kernel/Cargo.toml b/src/0x03/pkg/kernel/Cargo.toml index ee0edab..7205554 100644 --- a/src/0x03/pkg/kernel/Cargo.toml +++ b/src/0x03/pkg/kernel/Cargo.toml @@ -5,17 +5,17 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -boot = { package = "ysos_boot", path = "../boot", default-features = false } -lazy_static = { version = "1.4", features = ["spin_no_std"] } -uefi = { version = "0.33", default-features = false } -crossbeam-queue = { version = "0.3", default-features = false, features = ["alloc"] } -paste = "1.0" -spin = "0.9" -x86 = "0.52" -x86_64 = "0.15" -log = "0.4" -bitflags = "2.3" -bit_field = "0.10" -libm = "0.2" -linked_list_allocator = "0.10" -volatile = "0.6" +boot = { workspace = true } +lazy_static = { workspace = true } +uefi = { workspace = true } +crossbeam-queue = { workspace = true } +paste = { workspace = true } +spin = { workspace = true } +x86 = { workspace = true } +x86_64 = { workspace = true } +log = { workspace = true } +bitflags = { workspace = true } +bit_field = { workspace = true } +libm = { workspace = true } +linked_list_allocator = { workspace = true } +volatile = { workspace = true } diff --git a/src/0x04/Cargo.toml b/src/0x04/Cargo.toml index c12576b..b9cfcd1 100644 --- a/src/0x04/Cargo.toml +++ b/src/0x04/Cargo.toml @@ -16,3 +16,40 @@ debug = true [profile.release-with-debug.package."*"] debug = false + +[workspace.dependencies] +# NOTE: Remove or add dependencies here if you need +# This list is copied from final project + +bit_field = "0.10" +bitflags = "2.6" +hex-literal = "0.4" +libm = "0.2" +linked_list_allocator = "0.10" +log = "0.4" +lru = "0.12" +paste = "1.0" +pc-keyboard = "0.8" +rand_hc = "0.3" +spin = "0.9" +volatile = "0.6" +x86 = "0.52" +x86_64 = "0.15" +xmas-elf = "0.9" +uefi = { version = "0.33", default-features = false } +chrono = { version = "0.4", default-features = false } +arrayvec = { version = "0.7", default-features = false } +num_enum = { version = "0.7", default-features = false } +rand = { version = "0.8", default-features = false } +roaring = { version = "0.10", default-features = false } +lazy_static = { version = "1.4", features = ["spin_no_std"] } +micromath = { version = "2.0", features = ["num-traits"] } +futures-util = { version = "0.3", default-features = false, features = ["alloc"] } +crossbeam-queue = { version = "0.3", default-features = false, features = ["alloc"] } + +# Local dependencies + +lib = { path = "pkg/lib", package = "yslib" } +elf = { path = "pkg/elf", package = "ysos_elf" } +syscall_def = { path = "pkg/syscall", package = "ysos_syscall" } +boot = { path = "pkg/boot", default-features = false, package = "ysos_boot" } diff --git a/src/0x04/pkg/app/hello/Cargo.toml b/src/0x04/pkg/app/hello/Cargo.toml index 3a08b34..38e1290 100644 --- a/src/0x04/pkg/app/hello/Cargo.toml +++ b/src/0x04/pkg/app/hello/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -lib = { path="../../lib", package="yslib"} +lib = { workspace = true } diff --git a/src/0x04/pkg/lib/Cargo.toml b/src/0x04/pkg/lib/Cargo.toml index 74e2c50..bd22da8 100644 --- a/src/0x04/pkg/lib/Cargo.toml +++ b/src/0x04/pkg/lib/Cargo.toml @@ -4,4 +4,4 @@ version = "0.4.0" edition = "2021" [dependencies] -syscall_def = { package = "ysos_syscall", path = "../syscall" } +syscall_def = { workspace = true } diff --git a/src/0x04/pkg/syscall/Cargo.toml b/src/0x04/pkg/syscall/Cargo.toml index 5bb4369..a57798d 100644 --- a/src/0x04/pkg/syscall/Cargo.toml +++ b/src/0x04/pkg/syscall/Cargo.toml @@ -4,4 +4,4 @@ version = "0.4.0" edition = "2021" [dependencies] -num_enum = { version = "0.7", default-features = false } +num_enum = { workspace = true } diff --git a/src/0x05/pkg/app/counter/Cargo.toml b/src/0x05/pkg/app/counter/Cargo.toml index 495db03..f7174dc 100644 --- a/src/0x05/pkg/app/counter/Cargo.toml +++ b/src/0x05/pkg/app/counter/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -lib = { path="../../lib", package="yslib"} +lib = { workspace = true } diff --git a/src/0x06/pkg/storage/Cargo.toml b/src/0x06/pkg/storage/Cargo.toml index c0d82e8..9a43174 100644 --- a/src/0x06/pkg/storage/Cargo.toml +++ b/src/0x06/pkg/storage/Cargo.toml @@ -4,10 +4,10 @@ version = "0.6.0" edition = "2021" [dependencies] -log = "0.4" -spin = "0.9" -paste = "1.0" -bitflags = "2.0" -hex-literal = "0.4" -num_enum = { version = "0.7", default-features = false } -chrono = { version = "0.4", default-features = false, features = ["alloc"] } +hex-literal = { workspace = true } +paste = { workspace = true } +chrono = { workspace = true, features = ["alloc"] } +bitflags = { workspace = true } +log = { workspace = true } +spin = { workspace = true } +num_enum = { workspace = true } diff --git a/src/0x07/pkg/lib/Cargo.toml b/src/0x07/pkg/lib/Cargo.toml index 6b5d4b2..26fca85 100644 --- a/src/0x07/pkg/lib/Cargo.toml +++ b/src/0x07/pkg/lib/Cargo.toml @@ -4,9 +4,9 @@ version = "0.7.0" edition = "2021" [dependencies] -syscall_def = { package = "ysos_syscall", path = "../syscall" } -chrono = { version = "0.4", default-features = false } -linked_list_allocator = { version = "0.10", optional = true } +syscall_def = { workspace = true } +chrono = { workspace = true } +linked_list_allocator = { workspace = true, optional = true } [features] default = ["kernel_alloc"]