diff --git "a/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" "b/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" new file mode 100644 index 00000000000..17d2ec47505 --- /dev/null +++ "b/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" @@ -0,0 +1,9 @@ +--- +title: 2024 秋冬季开源操作系统训练营第一阶段总结-DFP-HN +date: 2024-11-10 14:01:09 +tags: + - author: DFP-HN + - repo: https://github.com/LearningOS/rust-rustlings-2024-autumn-DFP-HN +--- +# 第一阶段总结 +第一阶段训练营通过写一些rust的基本语句,快速入门rust,掌握了rust的基本语法。第一阶段的题目设置非常合理,对于rust零基础的也能快速入门,只需要一步一步按照文档上的学习就能轻松完成题目。 diff --git "a/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" "b/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" new file mode 100644 index 00000000000..3d50d062618 --- /dev/null +++ "b/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" @@ -0,0 +1,9 @@ +--- +title: 2024 秋冬季开源操作系统训练营第二阶段总结-DFP-HN +date: 2024-11-10 13:57:28 +tags: + - author: DFP-HN + - repo: https://github.com/LearningOS/2024a-rcore-DFP-HN +--- +# 第二阶段总结 +第二阶段的题目略有难度,我在这个阶段有了很大的收获。第三章的练习是获取当前任务的信息,通过这个练习,大致清楚了进程在操作系统中的运行流程以及组成结构,系统调用的实现和中断的实现。第四章的练习考察对页表的理解,在完成的过程中加深了我对虚拟地址和物理地址的理解。第五章的练习是实现一个子进程的创建,fork+exec也能生成一个子进程,但这个子进程的资源和空间与父进程完全一致,然而练习的要求需要子进程有独立的数据,如果采用fork+exec将浪费很多时间去重新加载数据。第六章的练习是关于文件系统,创建硬链接和取消硬链接,这一章节对我来说有点挑战,因为之前学操作系统对于文件系统这一块就没太搞懂,认真看了几个晚上的代码后才搞懂文件系统的结构,收获非常大。第八章练习比前面的轻松一些,只要搞清楚了代码中各个结构之间的关系,就能轻松解决。 \ No newline at end of file diff --git a/source/_posts/2024A-OS-I-Summary.md b/source/_posts/2024A-OS-I-Summary.md new file mode 100644 index 00000000000..ea90ae057fd --- /dev/null +++ b/source/_posts/2024A-OS-I-Summary.md @@ -0,0 +1,100 @@ +--- +title: 2024A-OS-I-Summary +date: 2024-11-10 12:34:18 +tags: + - author:Whth + - repo:https://github.com/LearningOS/rust-rustlings-2024-autumn-Whth + - RustBasics +--- + + + + + +# 引言 + +---- + +9月的时候在`Rust语言中文社区`的微信公众号了解到了这个训练营, +当时想想正好可以获取一些操作系统的设计与实践的经验和感受感受`Rust`编程的实际运用,所以就报名了. + + +# 第一阶段概述 + + +--- +## 个人情况 + +- 非科班 +- 大四备战考研ing +- 有过一些`C++`和`Python`的编程基础 +- 系统学习过linux的架构和原理 +- 了解过`Rust`的编程范式和语言特性(不过没怎么实际用过,只是简单的配过环境搓过些小玩意儿) + + + + +## Rustling + +说是100道题,不过实际上是80道题. +其中70道语言特性和语法相关的基础题目,而剩下的10道题都是算法相关的. + +因为`Rustling`没啥平台依赖所有就直接在win本把仓库克隆下来然后`cargo install&&rustling watch`开干 + +语法基础题花了一个下午给整完,中途卡了两天然后又花了两天把剩下的这10道题也做了.不过有一说一最后几个算法题确实有那么点上强度的感觉了Xb + +![img.png](2024A-OS-I-Summary/img.png) + +![img_1.png](2024A-OS-I-Summary/img_1.png) + + + +## 总结 + + + +唉唉,给我的感觉两类题目的难度曲线会稍微有点陡峭了,简单和困难的过渡不太平缓,中途如果没有`Jetbrain RustRover`的built-in linter(好用,推荐😋)可能还得多花些时间 +![img_2.png](2024A-OS-I-Summary/img_2.png) + +个人感觉最难的几个部分有生命周期,借用和作用域 + +1. **生命周期(Lifetimes)**:确保引用在其所指向的数据有效期内始终有效,防止悬空指针。虽说如此,但是实际上没有了`'a`和IDE的检查马上就歇菜了Xb. +2. **借用(Borrowing)**:允许临时使用数据而不转移所有权,提高代码的灵活性和安全性。这个借用和cpp的借用机制类似,不过在rust中 borrowing 的时候会自动释放,不用像cpp那样手动释放,挺好. +3. **作用域(Scopes)**:定义变量和引用的可见性和生命周期,确保资源在不再需要时被及时释放。最后我的理解感觉实际上也就是`{}`的范围. + +不过难归难,实际上rust里面挺多编程范式确实体验蛮不错的,我挺喜欢模式匹配,建造者模式,鼓励链式调用(手动点赞). + +最后在榜上捞了个Rank 39. + +![img_3.png](2024A-OS-I-Summary/img_3.png) + + +# 推荐阅读 + + + +1. [Rust官方文档](https://doc.rust-lang.org/book/title-page.html) - Rust的官方文档是最权威的学习资源之一,提供了详尽的语言特性和标准库介绍,适合所有级别的学习者。 + +2. [Rust精选](https://rustcc.cn/) - 提供了一系列聚焦于学习Rust的网络资源,包括但不限于使用Rust处理信号的文章,这些资源对于希望深入了解特定主题的学习者非常有帮助。 + +3. [《Rust 程序设计》](https://kaisery.github.io/trpl-zh-cn/) - 一本全面介绍Rust编程的书籍,适合希望系统学习Rust的新手。 + +4. [《Rust 权威指南》](https://rust-book.cs.dbappsecurity.com/) - 这本书深入讲解了Rust的核心概念和高级特性,适合已经有一定Rust基础的学习者。 + +5. [《Rust 实战》](https://rust-lang-nursery.github.io/rust-cookbook/intro.html) - 通过实战项目帮助读者掌握Rust编程的实际应用,适合希望通过实践加深理解的学习者。 + +6. [《深入理解 rust 并发编程》](https://rust-lang-nursery.github.io/rust-cookbook/concurrency.html) - 针对Rust的并发编程特性进行了详细的讲解,适合对并发编程感兴趣的开发者。 + +7. [《Command-Line Rust》](https://rust-cli.github.io/book/) - 专注于使用Rust开发命令行应用程序,适合对此类应用感兴趣的学习者。 + +8. [《Rust Atomics and Locks》](https://docs.rs/crossbeam/latest/crossbeam/atomic/index.html) - 深入讲解了Rust中的原子操作和锁机制,适合希望深入了解并发控制的学习者。 + +9. [《System Programing with Rust》](https://azerupi.github.io/mdBook/rust-system-programming/) - 介绍了如何使用Rust进行系统级编程,适合希望探索这一领域的开发者。 + +10. [《Rust 编程之道》](https://rustwiki.org/zh-CN/rust-by-example/) - 通过具体的例子和最佳实践,帮助读者更好地掌握Rust编程。 + + + + + + diff --git a/source/_posts/2024A-OS-I-Summary/img.png b/source/_posts/2024A-OS-I-Summary/img.png new file mode 100644 index 00000000000..0508f0d53b8 Binary files /dev/null and b/source/_posts/2024A-OS-I-Summary/img.png differ diff --git a/source/_posts/2024A-OS-I-Summary/img_1.png b/source/_posts/2024A-OS-I-Summary/img_1.png new file mode 100644 index 00000000000..7d5ed9c99b1 Binary files /dev/null and b/source/_posts/2024A-OS-I-Summary/img_1.png differ diff --git a/source/_posts/2024A-OS-I-Summary/img_2.png b/source/_posts/2024A-OS-I-Summary/img_2.png new file mode 100644 index 00000000000..131b0ec9b9d Binary files /dev/null and b/source/_posts/2024A-OS-I-Summary/img_2.png differ diff --git a/source/_posts/2024A-OS-I-Summary/img_3.png b/source/_posts/2024A-OS-I-Summary/img_3.png new file mode 100644 index 00000000000..c2cf8c5a43d Binary files /dev/null and b/source/_posts/2024A-OS-I-Summary/img_3.png differ diff --git a/source/_posts/2024A-OS-II-Summary.md b/source/_posts/2024A-OS-II-Summary.md new file mode 100644 index 00000000000..3e7ee47c215 --- /dev/null +++ b/source/_posts/2024A-OS-II-Summary.md @@ -0,0 +1,684 @@ +--- +title: 2024A-OS-II-Summary +date: 2024-11-10 12:34:26 +tags: + - auther:Whth + - repo:https://github.com/LearningOS/2024a-rcore-Whth + - OsBasics + - RustPractice +--- + + +* [引言](#引言) +* [在此之前](#在此之前) +* [第二阶段概述](#第二阶段概述) + * [环境配置](#环境配置) + * [战斗开始](#战斗开始) + * [ch1](#ch1) + * [问题与对应解决操作如下:](#问题与对应解决操作如下) + * [练习解题思路](#练习解题思路) + * [ch2](#ch2-) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-1) + * [练习解题思路](#练习解题思路-1) + * [ch3](#ch3) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-2) + * [练习解题思路](#练习解题思路-2) + * [ch4](#ch4) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-3) + * [练习解题思路](#练习解题思路-3) + * [ch5](#ch5) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-4) + * [练习解题思路](#练习解题思路-4) + * [ch6](#ch6) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-5) + * [练习解题思路](#练习解题思路-5) + * [ch7](#ch7) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-6) + * [练习解题思路](#练习解题思路-6) + * [ch8](#ch8) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-7) + * [练习解题思路](#练习解题思路-7) +* [总结](#总结) + + + +---- + +# 引言 + +到了第二阶段,强度又上了一个等级,这次直接开始学习手搓操作系统了,中途也算是被折磨了不少,但是也收获良多. + +切实的把学到的理论知识给使出来了,也提升了一些语言能力. + + +---- + +# 在此之前 +- 读`Rcore-book`的时候切记不要一开始就直接先按着它给全部啃完,然后再写代码,最好是代码和文档一起走,看着文档的内容介绍,然后再根据它来分析具体的实现. +- 多打`trace`|`log`|`assert`来调试,不要直接panic, 用`gdb`+`tmux`着实有点麻烦. + + +---- + +# 第二阶段概述 + +## 环境配置 + +文档里面指定了使用`Ubuntu 22`作为开发环境,而我的宿主机是`Windows 11`,所以最后就用`WSL2`了,装了一个轻量点的`Debian for WSL` + +使用字节跳动的 `rsproxy` 安装`rustup` + +```fish +# 修改配置 ~/.config/fish/config.fish + +echo "export RUSTUP_DIST_SERVER "https://rsproxy.cn"" >> ~/.config/fish/config.fish +echo "export RUSTUP_UPDATE_ROOT "https://rsproxy.cn/rustup"" >> ~/.config/fish/config.fish + +``` + +安装 `rustup` +```fish +curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh + +``` + +修改 `~/.cargo/config.toml` 文件,配置`rsproxy`为默认源 +``` +[source.crates-io] +replace-with = 'rsproxy-sparse' +[source.rsproxy] +registry = "https://rsproxy.cn/crates.io-index" +[source.rsproxy-sparse] +registry = "sparse+https://rsproxy.cn/index/" +[registries.rsproxy] +index = "https://rsproxy.cn/crates.io-index" +[net] +git-fetch-with-cli = true +``` + +安装 `qemu-system-riscv64` 和 `qemu-riscv` + +```fish +# 安装依赖 +sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ + gawk build-essential bison flex texinfo gperf libtool patchutils bc \ + zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3 +# 下载并编译安装 qemu +wget https://download.qemu.org/qemu-7.0.0.tar.xz &&\ +tar xf qemu-7.0.0.tar.xz &&\ +cd qemu-7.0.0 &&\ +./configure --target-list=riscv64-softmmu,riscv64-linux-user && \ +make -j$(nproc) && sudo make install + +``` + +克隆`rcore-os`仓库,并且克隆测试用例仓库 +```fish +git clone https://github.com/LearningOS/2024a-rcore-Whth.git && \ +cd 2024a-rcore-Whth && \ +git clone https://github.com/LearningOS/rCore-Tutorial-Test-2024A user --depth 1 &&\ +git clone https://github.com/LearningOS/rCore-Tutorial-Checker-2024A.git ci-user --depth 1 &&\ +cp -r user ci-user/user +``` + + +运行第一个例子 +```fish +git checkout ch1 &&\ +cd os &&\ +make run +``` + +Success! +```fish +[rustsbi] RustSBI version 0.3.0-alpha.4, adapting to RISC-V SBI v1.0.0 +.______ __ __ _______.___________. _______..______ __ +| _ \ | | | | / | | / || _ \ | | +| |_) | | | | | | (----`---| |----`| (----`| |_) || | +| / | | | | \ \ | | \ \ | _ < | | +| |\ \----.| `--' |.----) | | | .----) | | |_) || | +| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__| +[rustsbi] Implementation : RustSBI-QEMU Version 0.2.0-alpha.2 +[rustsbi] Platform Name : riscv-virtio,qemu +[rustsbi] Platform SMP : 1 +[rustsbi] Platform Memory : 0x80000000..0x88000000 +[rustsbi] Boot HART : 0 +[rustsbi] Device Tree Region : 0x87000000..0x87000ef2 +[rustsbi] Firmware Address : 0x80000000 +[rustsbi] Supervisor Address : 0x80200000 +[rustsbi] pmp01: 0x00000000..0x80000000 (-wr) +[rustsbi] pmp02: 0x80000000..0x80200000 (---) +[rustsbi] pmp03: 0x80200000..0x88000000 (xwr) +[rustsbi] pmp04: 0x88000000..0x00000000 (-wr) +[kernel] Hello, world! +``` + +## 战斗开始 + +> 注: 为了快速了解主要功能,使用了LLM来提取要点,仅作为参考, 不保证100%正确 + +### ch1 + +>本章主要内容是构建一个用户态和裸机的最小执行环境,包括理解执行环境、修改目标平台、移除标准库依赖、实现基本功能(如输出字符串和关机),并正确配置内存布局和栈空间,最终实现一个能够运行简单应用程序的环境。 + +#### 问题与对应解决操作如下: + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|-----------------------------------------------------|-----------------------------------------------------------------------|----------------------------------------------------|--------------------------| +| 无法成功将`Hello World!` 移植到`riscv64gc-unknown-none-elf` | 报错的原因是目标平台上确实没有 Rust 标准库 std,也不存在任何受 OS 支持的系统调用,也就是裸机平台 (bare-metal)。 | 将标准库 `std` 替换为无平台依赖的核心库 `core`,以满足裸机平台的编译需求。 | 可以通过编译啦! | +| 通过编译后的程序是空程序,怎么让它变得可以执行用户代码? | 没有定义入口函数 | 添加`_start`作为入口函数 | 可以编译并且可以执行空的`loop` | +| `_start`编译得到的程序并不能正常退出,报段错误了,为什么? | U模式下尝试越权执行S指令导致的 | 根据`riscv`的指令标准,使用ecall完成权限的正常跳转,添加一个`syscall_exit` | 可以编译并且正常退出啦! | +| 没有显示很不好调试啊,怎么加一个? | `core`库里面并没有实现`println!` | 添加一个`syscall_write`和`stdout`用作实现`println!` | 可以正常显示输出啦! | | | | +| 现在的程序仍然是在用户态上运行的,而操作系统需要在内核态上运行,怎么解决? | 程序默认在用户态运行,没有切换到内核态。 | 修改程序入口点和初始化代码,确保程序在内核态运行。 | 程序在内核态运行,能够访问内核态资源。 | +| 内存布局不符合预期,直接退出了,怎么办? | 缺少或错误的链接脚本配置。 | 使用链接脚本 (Linker Script) 调整内存布局,使入口地址为 0x80200000。 | 内存布局符合预期,程序能够在指定地址开始执行。 | +| 未正确设置栈空间 | 没有为操作系统预留足够的栈空间,或者没有正确初始化栈指针。 | 在汇编文件中预留栈空间,并在启动时设置栈指针。 | 栈空间正确设置,程序可以安全地使用栈。 | +| .bss 段未清零 | .bss 段没有被初始化为零,可能导致未定义行为。 | 在 `rust_main` 函数中调用 `clear_bss` 函数清零 .bss 段。 | .bss 段被正确清零,避免了潜在的未定义行为。 | +| 缺少打印功能 | 没有实现或适配内核态的打印功能。 | 修改用户态的 `println` 宏,实现内核态的打印功能。 | 可以在内核态打印信息,便于调试。 | +| 异常处理时未打印错误位置 | 异常处理函数 `panic` 没有实现打印错误位置的功能。 | 重写 `panic` 函数,使其在异常时打印错误发生的位置。 | 异常时能够打印错误位置,方便定位问题。 | +| 日志功能缺失 | 没有集成日志库,或日志库未正确配置。 | 在项目中引入日志库 `log` 并实现日志模块。 | 可以记录不同级别的日志信息,增强调试能力。 | + +至此,我们完成了一个可以执行用户态代码的裸机程序😋。 +```fish +>>> cd os&&make run LOG=DEBUG + +[rustsbi] RustSBI version 0.3.0-alpha.4, adapting to RISC-V SBI v1.0.0 +.______ __ __ _______.___________. _______..______ __ +| _ \ | | | | / | | / || _ \ | | +| |_) | | | | | | (----`---| |----`| (----`| |_) || | +| / | | | | \ \ | | \ \ | _ < | | +| |\ \----.| `--' |.----) | | | .----) | | |_) || | +| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__| +[rustsbi] Implementation : RustSBI-QEMU Version 0.2.0-alpha.2 +[rustsbi] Platform Name : riscv-virtio,qemu +[rustsbi] Platform SMP : 1 +[rustsbi] Platform Memory : 0x80000000..0x88000000 +[rustsbi] Boot HART : 0 +[rustsbi] Device Tree Region : 0x87000000..0x87000ef2 +[rustsbi] Firmware Address : 0x80000000 +[rustsbi] Supervisor Address : 0x80200000 +[rustsbi] pmp01: 0x00000000..0x80000000 (-wr) +[rustsbi] pmp02: 0x80000000..0x80200000 (---) +[rustsbi] pmp03: 0x80200000..0x88000000 (xwr) +[rustsbi] pmp04: 0x88000000..0x00000000 (-wr) +[kernel] Hello, world! +[DEBUG] [kernel] .rodata [0x80202000, 0x80203000) +[ INFO] [kernel] .data [0x80203000, 0x80204000) +[ WARN] [kernel] boot_stack top=bottom=0x80214000, lower_bound=0x80204000 +[ERROR] [kernel] .bss [0x80214000, 0x80215000) +``` + + +#### 练习解题思路 +- undefined + +---- + +### ch2 + +>本章主要内容是实现一个简单的批处理系统,包括设计和实现应用程序、管理内存布局、实现系统调用、编译生成应用程序二进制码、将应用程序链接到内核、实现特权级切换和 Trap 管理,最终实现内核加载并执行用户态应用程序的功能。 + + +#### 问题与对应解决操作如下: + + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|----------------------|-----------------------------------------|---------------------------------------------------------------------|----------------------------------| +| 应用程序设计看起来好复杂啊! | 用户库预留了很多未来才会用到的系统调用接口,还有一些高级功能。 | 只关注本节提到的部分,忽略那些未来的神秘东西。 | 简化了开发过程,让你能快速上手。 | +| 找不到 main 函数,编译器报错了! | 默认情况下,Rust 程序需要一个 main 函数作为入口。 | 移除 main 函数,使用自定义的 _start 函数。 | 程序不再依赖标准库的 main 函数,自由度更高。 | +| 怎么输出个字符串这么难? | 标准库的 println! 宏依赖于标准 I/O,裸机环境下没有这些支持。 | 自己实现一个输出字符串的函数。 | 可以在裸机环境下愉快地打印字符串了! | +| 程序跑起来,但是一直不退出,卡在那里了! | 缺少一个优雅的退出机制。 | 实现一个关机功能,让程序能够正常退出。 | 程序运行完后,QEMU 会优雅地退出,不再卡在那里。 | +| 内存布局怎么这么乱? | 缺少或错误的链接脚本配置。 | 使用链接脚本 (Linker Script) 调整内存布局。 | 内存布局变得整洁有序,程序在指定地址开始执行。 | +| 栈空间怎么总是出问题? | 没有为操作系统预留足够的栈空间,或者没有正确初始化栈指针。 | 在汇编文件中预留栈空间,并在启动时设置栈指针。 | 栈空间正确设置,程序可以安全地使用栈。 | +| .bss 段里的垃圾数据怎么清理? | .bss 段没有被初始化为零,可能导致未定义行为。 | 在 `rust_main` 函数中调用 `clear_bss` 函数清零 .bss 段。 | .bss 段被正确清零,避免了潜在的未定义行为。 | +| 怎么和内核通信? | 应用程序需要调用内核提供的系统调用。 | 实现系统调用接口,使用 ecall 指令。 | 应用程序可以通过系统调用与内核通信,完成各种任务。 | +| 怎么编译生成应用程序二进制码? | 缺少编译和链接的步骤。 | 使用 Cargo 和 objcopy 工具编译生成二进制文件。 | 生成了可以在裸机上运行的应用程序二进制文件。 | +| 怎么把应用程序链接到内核? | 应用程序的二进制镜像文件需要作为数据段链接到内核里,内核需要知道它们的位置。 | 使用汇编文件 `link_app.S` 将应用程序的二进制镜像链接到内核。 | 应用程序的二进制镜像被正确链接到内核中。 | +| 找不到应用程序的起始和结束位置? | 内核需要知道应用程序的数量和它们的起始和结束位置。 | 在 `link_app.S` 中定义全局符号,记录应用程序的数量和位置。 | 内核能够准确找到每个应用程序的起始和结束位置。 | +| 如何初始化应用管理器? | 需要一个全局的应用管理器来管理应用程序的加载和运行。 | 使用 `lazy_static!` 宏创建全局的 `AppManager` 实例。 | 应用管理器在第一次使用时自动初始化。 | +| 如何加载应用程序? | 需要将应用程序的二进制镜像加载到指定的内存地址。 | 实现 `load_app` 方法,将应用程序加载到 0x80400000 起始的内存地址。 | 应用程序被正确加载到内存中,准备运行。 | +| i-cache 不一致怎么办? | 修改内存中的代码段时,i-cache 中的内容可能与内存不一致。 | 在加载应用程序前,使用 `fence.i` 指令清空 i-cache。 | 保证程序执行的正确性,避免 i-cache 与内存不一致的问题。 | +| 怎么管理多个应用程序? | 需要一个机制来管理多个应用程序的加载和运行。 | 在 `AppManager` 中维护应用程序的数量和当前运行的应用程序索引。 | 能够按顺序加载和运行多个应用程序。 | +| 如何实现批处理操作系统的接口? | 需要提供接口来初始化和运行下一个应用程序。 | 实现 `init` 和 `run_next_app` 方法,分别用于初始化和运行下一个应用程序。 | 批处理操作系统能够初始化并按顺序运行多个应用程序。 | +| 怎么实现特权级切换? | 批处理操作系统需要在用户态和内核态之间切换,以便初始化、监控和处理应用程序。 | 使用 RISC-V 的特权级切换机制,通过控制状态寄存器(CSR)和硬件支持。 | 实现了用户态和内核态之间的平滑切换。 | +| 特权级切换的起因是什么? | 需要在启动应用程序、处理系统调用、处理错误和应用程序结束时进行特权级切换。 | 确保在这些关键时刻能够正确切换特权级。 | 确保了批处理操作系统的稳定性和安全性。 | +| 特权级切换涉及哪些控制状态寄存器? | 需要管理特权级切换时的各种状态信息。 | 使用 sstatus、sepc、scause、stval 和 stvec 等 CSR。 | 确保了 Trap 处理的正确性和可靠性。 | +| 如何处理 Trap 进入 S 特权级? | CPU 需要从用户态切换到内核态处理 Trap。 | 硬件自动修改 sstatus、sepc、scause 和 stval 寄存器,并跳转到 stvec 指定的入口地址。 | 确保了 Trap 处理的高效性和准确性。 | +| 如何处理 Trap 返回用户态? | Trap 处理完成后需要返回用户态继续执行。 | 使用 sret 指令恢复 sstatus 和 sepc 寄存器,并跳转回用户态。 | 确保了 Trap 处理后的正确返回。 | +| 用户栈和内核栈有什么区别? | Trap 发生时需要切换栈,以保存和恢复寄存器状态。 | 定义了 `KernelStack` 和 `UserStack` 类型,并实现 `get_sp` 方法获取栈顶地址。 | 确保了 Trap 处理时栈的正确管理和切换。 | +| 如何保存和恢复 Trap 上下文? | Trap 发生时需要保存当前的寄存器状态,Trap 处理后需要恢复这些状态。 | 定义 `TrapContext` 结构体,包含所有通用寄存器和必要的 CSR。 | 确保了 Trap 处理前后寄存器状态的一致性。 | +| 具体如何保存 Trap 上下文? | Trap 发生时需要保存当前的寄存器状态,以便 Trap 处理后恢复。 | 在 `__alltraps` 汇编函数中保存所有通用寄存器和必要的 CSR。 | 确保了 Trap 处理前寄存器状态的正确保存。 | +| 具体如何设置 Trap 处理入口? | 需要指定 Trap 处理的入口地址。 | 在 `trap::init` 中设置 `stvec` 寄存器指向 `__alltraps`。 | 确保了 Trap 处理的入口地址正确设置。 | +| 具体如何恢复 Trap 上下文? | Trap 处理完成后需要恢复寄存器状态,继续执行应用程序。 | 在 `__restore` 汇编函数中恢复所有通用寄存器和必要的 CSR。 | 确保了 Trap 处理后寄存器状态的正确恢复。 | +| 如何处理系统调用? | 应用程序通过 ecall 指令触发系统调用,需要在内核态处理。 | 在 `trap_handler` 中识别 ecall 并调用 `syscall` 函数。 | 正确处理了系统调用请求。 | +| 如何处理访存错误和非法指令? | 应用程序可能出现访存错误或非法指令,需要妥善处理。 | 在 `trap_handler` 中捕获并处理这些错误,调用 `run_next_app`。 | 确保了应用程序出错时能够正确处理并切换到下一个应用程序。 | +| 如何实现具体的系统调用? | 需要根据 syscall ID 调用相应的处理函数。 | 在 `syscall` 模块中实现 `sys_write` 和 `sys_exit`。 | 提供了基本的系统调用功能。 | +| 如何打印系统调用的结果? | 需要将系统调用的结果输出到控制台。 | 在 `sys_write` 中将缓冲区内容转换为字符串并打印。 | 确保了系统调用结果的正确输出。 | +| 如何处理应用程序退出? | 应用程序退出时需要通知内核并切换到下一个应用程序。 | 在 `sys_exit` 中打印退出信息并调用 `run_next_app`。 | 确保了应用程序退出后的正确处理和切换。 | +| 如何执行下一个应用程序? | 当批处理操作系统初始化完成或某个应用程序运行结束时,需要切换到下一个应用程序。 | 在 `run_next_app` 函数中加载并运行下一个应用程序。 | 确保了应用程序的连续运行。 | +| 如何跳转到应用程序入口点? | 需要将 CPU 的执行跳转到应用程序的入口点 0x80400000。 | 在 `TrapContext::app_init_context` 中设置 `sepc` 为 0x80400000。 | 确保了 CPU 跳转到应用程序的入口点。 | +| 如何切换到用户栈? | 需要将使用的栈从内核栈切换到用户栈。 | 在 `TrapContext::app_init_context` 中设置 `sp` 为用户栈的栈顶地址。 | 确保了 CPU 使用用户栈。 | +| 如何设置 sscratch 指向内核栈? | 在 Trap 处理时需要确保 sscratch 指向内核栈。 | 在 `__restore` 函数中设置 `sscratch` 为内核栈的栈顶地址。 | 确保了 sscratch 指向内核栈。 | +| 如何从 S 特权级切换到 U 特权级? | 需要从 S 特权级切换到 U 特权级以运行应用程序。 | 在 `TrapContext::app_init_context` 中设置 `sstatus` 的 `SPP` 字段为 `User`。 | 确保了特权级从 S 切换到 U。 | +| 如何复用 __restore 代码? | 需要复用 `__restore` 代码来简化启动应用程序的过程。 | 在 `run_next_app` 中调用 `__restore` 并传递一个特殊构造的 Trap 上下文。 | 确保了启动应用程序的简洁性和一致性。 | + +至此我们的OS可以承载多个用户态程序,并且可以切换用户态程序,并且可以返回用户态程序。 + +#### 练习解题思路 +- undefined +- +---- + +### ch3 + +>主要介绍了多道程序与分时多任务的概念,包括多道程序的放置与加载、任务切换的实现、任务管理机制、系统调用如yield和exit的实现、以及在RISC-V架构下处理嵌套中断和实现抢占式调度的方法。 + + +#### 问题与对应解决操作如下: + +| 问题 | 原因 | 解决方案 | 最终效果 | +|-----------------------------|------------------------------------|------------------------------------------------------------------|--------------------------| +| 如何放置多个应用程序? | 内核需要让每个应用程序加载到不同的内存地址,以支持多道程序。 | 为每个应用程序指定不同的起始地址。 | 确保了多个应用程序可以同时驻留在内存中。 | +| 如何为每个应用程序指定不同的起始地址? | 每个应用程序需要有自己的起始地址,以避免冲突。 | 使用 `user/build.py` 脚本为每个应用程序指定起始地址。 | 确保了每个应用程序的起始地址不同。 | +| 如何加载多个应用程序? | 需要在内核初始化时将所有应用程序加载到内存中。 | 在 `loader` 模块的 `load_apps` 函数中加载所有应用程序。 | 确保了所有应用程序在内核初始化时被加载到内存中。 | +| 如何清空应用程序区域? | 在加载新应用程序之前,需要清空内存区域以避免残留数据。 | 在 `load_apps` 函数中清空内存区域。 | 确保了内存区域在加载新应用程序前被清空。 | +| 如何加载应用程序到指定地址? | 需要将应用程序的二进制数据加载到指定的内存地址。 | 在 `load_apps` 函数中将应用程序数据复制到指定地址。 | 确保了应用程序被正确加载到指定的内存地址。 | +| 如何计算应用程序的基地址? | 需要为每个应用程序计算一个唯一的基地址。 | 在 `get_base_i` 函数中计算应用程序的基地址。 | 确保了每个应用程序的基地址唯一。 | +| 如何实现任务切换? | 任务切换是操作系统的核心机制,需要在应用运行中交出 CPU 使用权。 | 设计和实现任务切换机制,包括保存和恢复任务上下文。 | 确保了多个任务之间的平滑切换。 | +| 任务切换与 Trap 切换有何不同? | 任务切换和 Trap 切换在某些方面有相似之处,但也有一些关键差异。 | 任务切换不涉及特权级切换,部分由编译器完成。 | 确保了任务切换的高效性和透明性。 | +| 如何保存任务上下文? | 任务切换时需要保存当前任务的寄存器状态。 | 在 `__switch` 汇编函数中保存 `ra`、`sp` 和 `s0~s11`。 | 确保了任务上下文的正确保存。 | +| 如何恢复任务上下文? | 任务切换时需要恢复目标任务的寄存器状态。 | 在 `__switch` 汇编函数中恢复 `ra`、`sp` 和 `s0~s11`。 | 确保了任务上下文的正确恢复。 | +| 如何实现 `__switch` 函数? | 需要一个函数来完成任务上下文的保存和恢复。 | 在 `switch.S` 中实现 `__switch` 汇编函数。 | 确保了任务切换的高效性和可靠性。 | +| 如何定义 `TaskContext` 结构体? | 需要一个结构体来存储任务上下文。 | 在 `task/context.rs` 中定义 `TaskContext` 结构体。 | 确保了任务上下文的正确表示。 | +| 如何在 Rust 中调用 `__switch` 函数? | 需要在 Rust 代码中调用 `__switch` 函数。 | 在 `task/switch.rs` 中声明 `__switch` 外部函数。 | 确保了 `__switch` 函数的正确调用。 | +| 如何管理任务? | 内核需要管理多个任务,包括任务的状态和上下文。 | 维护任务控制块和任务管理器。 | 确保了任务的有效管理和调度。 | +| 任务运行状态有哪些? | 任务需要有不同的运行状态来管理其生命周期。 | 定义 `TaskStatus` 枚举,包括 `UnInit`、`Ready`、`Running` 和 `Exited`。 | 确保了任务状态的清晰表示。 | +| 任务控制块包含哪些信息? | 任务控制块需要存储任务的状态和上下文。 | 在 `TaskControlBlock` 结构体中包含 `task_status` 和 `task_cx`。 | 确保了任务控制块的完整性和一致性。 | +| 如何实现任务管理器? | 需要一个全局的任务管理器来管理任务控制块。 | 创建 `TaskManager` 结构体,包含任务控制块数组和当前任务索引。 | 确保了任务管理器的全局可见性和管理能力。 | +| 如何初始化任务管理器? | 任务管理器需要在内核初始化时进行初始化。 | 在 `TASK_MANAGER` 的 `lazy_static!` 块中初始化任务管理器。 | 确保了任务管理器的正确初始化。 | +| 如何实现 `sys_yield` 系统调用? | 应用需要主动交出 CPU 使用权。 | 实现 `sys_yield` 函数,调用 `suspend_current_and_run_next`。 | 确保了应用可以主动交出 CPU 使用权。 | +| 如何实现 `sys_exit` 系统调用? | 应用需要主动退出。 | 实现 `sys_exit` 函数,调用 `exit_current_and_run_next`。 | 确保了应用可以主动退出。 | +| 如何实现任务切换? | 任务切换需要保存和恢复任务上下文。 | 实现 `__switch` 汇编函数,保存和恢复 `ra`、`sp` 和 `s0~s11`。 | 确保了任务切换的高效性和可靠性。 | +| 如何初始化任务上下文? | 任务控制块需要初始化任务上下文。 | 在 `init_app_cx` 中初始化 Trap 上下文,并在 `goto_restore` 中设置 `ra` 和 `sp`。 | 确保了任务上下文的正确初始化。 | +| 如何执行第一个任务? | 需要从任务管理器中选择并执行第一个任务。 | 在 `run_first_task` 中调用 `__switch` 切换到第一个任务的上下文。 | 确保了第一个任务的正确执行。 | +| 如何实现分时多任务系统? | 现代任务调度算法需要抢占式调度,每个应用只能连续执行一段时间。 | 使用时间片轮转算法 (RR) 对应用进行调度。 | 确保了任务的公平调度。 | +| 如何实现时钟中断与计时器? | 实现调度算法需要计时。 | 使用 RISC-V 的 mtime 和 mtimecmp 寄存器。 | 确保了精确的计时和时钟中断。 | +| 如何获取当前时间? | 应用需要获取当前时间。 | 实现 `get_time` 和 `get_time_us` 函数。 | 确保了应用可以获取当前时间。 | +| 如何设置时钟中断? | 需要设置时钟中断来触发任务调度。 | 实现 `set_timer` 和 `set_next_trigger` 函数。 | 确保了时钟中断的正确设置。 | +| 如何处理时钟中断? | 时钟中断触发时需要进行任务切换。 | 在 `trap_handler` 中处理 S 特权级时钟中断。 | 确保了时钟中断触发后的任务切换。 | +| 如何启用时钟中断? | 需要启用 S 特权级时钟中断以避免被屏蔽。 | 在 `rust_main` 中调用 `enable_timer_interrupt`。 | 确保了 S 特权级时钟中断的启用。 | + + +至此,我们完成了一个简单的内核,它已经并行执行了多个应用 + +#### 练习解题思路 + +- 直接为每一个task打上数据标签保存所有的`taskinfo` 相关信息. +- 额外的为了正常统计syscall计数需要为syscall添加一个 `hook`用作每次`syscall`被调用时可以更新当前`TaskControlBlock`的syscall计数 +- 最后实现 `syscall_get_task_info`函数本体,从`TaskControlBlock`哪里取出数据返回即可. + +---- + +### ch4 + +>本章深入探讨了RISC-V架构下的SV39多级页表机制的实现,包括虚拟地址和物理地址的管理、内存控制相关的CSR寄存器、地址格式与组成等,并详细介绍了如何实现地址空间抽象、内核与应用的地址空间管理以及基于地址空间的分时多任务处理。 + + +#### 问题与对应解决操作如下: + +| 问题 | 原因 | 解决方案 | 最终效果 | +|---------------------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------| +| 如何实现 SV39 多级页表机制? | 就像给房子建楼层一样,我们需要在内核中搭建 RV64 架构的 SV39 分页系统。 | 分步骤来,先打地基(准备环境),再一层一层往上建。 | 我们就有了一个稳固的多层建筑,即实现了 SV39 分页机制。 | +| 虚拟地址和物理地址的区别是什么? | 访问内存时,就像找人,得知道是在虚拟世界还是现实世界找。 | 修改 satp 这个特殊的开关,告诉系统我们要在哪个世界里玩。 | 系统就能正确地把虚拟世界的地址翻译成现实世界的地址了。 | +| 如何启用分页模式? | 默认情况下,MMU(内存管理单元)这个大管家没上班,我们需要叫它起来工作。 | 把 satp 的 MODE 字段设置为 8,就像是给大管家发了个开工通知。 | 这样,大管家就正式上岗,开始处理分页事务了。 | +| 如何定义地址和页号的类型? | 在编程的世界里,给东西起名字很重要,尤其是地址和页号这种关键角色。 | 用 Rust 语言定义 `PhysAddr`、`VirtAddr`、`PhysPageNum` 和 `VirtPageNum` 四个小伙伴。 | 这样每个小伙伴都有了自己的身份证明,方便管理。 | +| 如何实现地址和页号之间的转换? | 地址和页号之间需要相互认识,好比是不同语言的人交流。 | 为它们实现 `From` 和 `Into` 特性,还有 `floor` 和 `ceil` 方法,就像教它们一门通用语言。 | 这样,地址和页号就可以无障碍沟通了。 | +| 如何定义页表项的数据结构? | 页表项是页表里的小兵,要有自己的样子。 | 使用 `bitflags` 工具箱来定义 `PTEFlags` 和 `PageTableEntry`,给小兵们穿上制服。 | 这样,每个小兵都有了独特的身份标识。 | +| 如何生成页表项? | 小兵们需要有出生证,证明它们是合法的。 | 为 `PageTableEntry` 添加 `new` 和 `empty` 方法,就像是给小兵们颁发出生证。 | 这样,小兵们就有了合法的身份。 | +| 如何检查页表项的标志位? | 小兵们的任务和能力需要定期检查。 | 为 `PageTableEntry` 添加 `is_valid` 方法,就像是对小兵们的能力进行审查。 | 这样,我们就能确保每个小兵都在正确地执行任务。 | +| 如何管理物理页帧? | 物理页帧就像是仓库里的货物,需要有人管理。 | 实现 `StackFrameAllocator`,就像请了一个仓库管理员。 | 这样,货物的进出就有条不紊了。 | +| 如何确定可用物理内存范围? | 仓库有多大,哪些地方可以存放货物,这些都是需要事先规划好的。 | 在链接脚本 `os/src/linker.ld` 里用 `ekernel` 符号标出内核数据的终点。 | 这样,我们就明确了哪些地方是可以用来存放货物的。 | +| 如何定义物理页帧管理器的 Trait? | 为了规范仓库管理员的行为,需要有一套标准。 | 定义 `FrameAllocator` Trait,就像是制定了仓库管理的标准流程。 | 这样,不管谁来当仓库管理员,都能按规矩办事。 | +| 如何实现 `StackFrameAllocator`? | 有了标准,还需要有一个具体的实施者。 | 实现 `StackFrameAllocator` 结构体及相关方法,就像是选定了一个具体的仓库管理员。 | 这样,货物的管理就有了实际的操作者。 | +| 如何初始化 `StackFrameAllocator`? | 仓库管理员上任前,需要了解仓库的情况。 | 为 `StackFrameAllocator` 实现 `new` 和 `init` 方法,就像是管理员做入职培训。 | 这样,管理员就能快速进入状态,开始工作。 | +| 如何实现物理页帧的分配? | 仓库管理员要懂得如何合理分配货物。 | 为 `StackFrameAllocator` 实现 `alloc` 方法,就像是训练管理员如何发放货物。 | 这样,货物的分配就更加合理有效了。 | +| 如何实现物理页帧的回收? | 当货物不再需要时,要能够及时回收,以便再次利用。 | 为 `StackFrameAllocator` 实现 `dealloc` 方法,就像是教管理员如何回收货物。 | 这样,仓库的空间利用率就提高了。 | +| 如何创建物理页帧的全局实例? | 仓库需要有一个总管,随时调用资源。 | 创建 `FRAME_ALLOCATOR` 全局实例,并完成初始化。 | 这样,任何地方都可以调用总管来获取或归还资源了。 | +| 如何实现物理页帧分配/回收的接口? | 总管需要提供简单直接的服务窗口。 | 实现 `frame_alloc` 和 `frame_dealloc` 函数,就像是开设了服务窗口。 | 这样,用户就能很方便地请求或归还资源了。 | +| 如何实现 `FrameTracker`? | 为了确保货物的安全,需要有个追踪系统。 | 实现 `FrameTracker` 结构体及相关方法,就像是建立了一套货物追踪系统。 | 这样,货物的状态就能实时监控了。 | +| 如何实现 `FrameTracker` 的自动回收? | 当货物不再使用时,应该能够自动归还仓库。 | 为 `FrameTracker` 实现 `Drop` 特性,就像是给货物装上了自动归还装置。 | 这样,货物就能自动回到仓库,减少了人为操作。 | +| 如何实现多级页表的基本数据结构? | 多级页表就像是一栋多层楼的大厦,需要设计好每一层的布局。 | 定义 `PageTable` 结构体及相关方法,就像是设计好了大厦的蓝图。 | 这样,大厦的建设就有了依据。 | +| 如何初始化多级页表? | 大厦建设前,需要打好地基。 | 为 `PageTable` 实现 `new` 方法,就像是为大厦打好地基。 | 这样,大厦的建设就有了坚实的基础。 | +| 如何实现多级页表的映射和解除映射? | 大厦里的房间需要能够灵活地分配和收回。 | 为 `PageTable` 实现 `map` 和 `unmap` 方法,就像是制定了房间分配和收回的规则。 | 这样,房间的管理就更加灵活高效了。 | +| 如何在多级页表中查找页表项? | 在多层楼的大厦里找到特定的房间,需要有一张详细的楼层图。 | 为 `PageTable` 实现 `find_pte_create` 方法,就像是制作了一份详细的楼层指南。 | 这样,就能快速准确地找到目标房间了。 | +| 如何访问物理页帧? | 要想进入仓库取货,需要有专门的通道。 | 为 `PhysPageNum` 实现 `get_pte_array`、`get_bytes_array` 和 `get_mut` 方法,就像是设置了专用的取货通道。 | 这样,取货就更加便捷了。 | +| 如何实现恒等映射? | 有时候,最简单的办法就是最好的。 | 使用恒等映射,就像是让虚拟页号和物理页号一一对应。 | 这样,系统就能以最简单的方式工作了。 | +| 如何实现手动查页表的方法? | 有时候,我们需要绕过系统,直接从底层获取信息。 | 为 `PageTable` 实现 `from_token`、`find_pte` 和 `translate` 方法,就像是掌握了直达底层的密道。 | 这样,即使没有系统的帮助,也能快速获取所需的信息。 | +| 如何实现地址空间的抽象? | 地址空间就像是一个大容器,里面装着很多小容器。 | 定义 `MapArea` 和 `MemorySet` 结构体及方法,就像是设计了一个可以装很多小容器的大箱子。 | 这样,地址空间的管理就更加有序了。 | +| 如何描述逻辑段? | 每个小容器都有自己的用途,需要清楚地标记出来。 | 定义 `MapArea` 结构体,就像是给每个小容器贴上了标签。 | 这样,每个小容器的用途就一目了然了。 | +| 如何实现逻辑段的映射方式? | 不同的小容器可能有不同的打开方式。 | 定义 `MapType` 枚举,就像是给每种小容器设定了打开方式。 | 这样,无论哪种小容器,都能轻松打开。 | +| 如何实现逻辑段的权限控制? | 每个小容器都有自己的门锁,需要控制谁能开锁。 | 定义 `MapPermission` 结构体,就像是给每个小容器安装了智能门锁。 | 这样,只有被授权的人才能打开小容器。 | +| 如何实现地址空间的管理? | 管理一个大容器,需要有一个好的计划。 | 定义 `MemorySet` 结构体及其方法,就像是制定了一份管理大容器的计划书。 | 这样,大容器内的所有小容器就能得到有效的管理了。 | +| 如何初始化地址空间? | 需要初始化地址空间。 | 实现 `MemorySet::new_bare` 方法。 | 确保了地址空间的正确初始化。 | +| 如何在地址空间中插入逻辑段? | 需要在地址空间中插入新的逻辑段。 | 实现 `MemorySet::push` 方法。 | 确保了逻辑段的正确插入。 | +| 如何插入 Framed 方式的逻辑段? | 需要在地址空间中插入 Framed 方式的逻辑段。 | 实现 `MemorySet::insert_framed_area` 方法。 | 确保了 Framed 方式逻辑段的正确插入。 | +| 如何生成内核的地址空间? | 需要生成内核的地址空间。 | 实现 `MemorySet::new_kernel` 方法。 | 确保了内核地址空间的生成。 | +| 如何从 ELF 文件生成应用的地址空间? | 需要从 ELF 文件生成应用的地址空间。 | 实现 `MemorySet::from_elf` 方法。 | 确保了应用地址空间的生成。 | +| 如何实现逻辑段的映射? | 需要实现逻辑段的映射。 | 实现 `MapArea::map` 方法。 | 确保了逻辑段的正确映射。 | +| 如何实现逻辑段的解映射? | 需要实现逻辑段的解映射。 | 实现 `MapArea::unmap` 方法。 | 确保了逻辑段的正确解映射。 | +| 如何实现逻辑段的数据拷贝? | 需要实现逻辑段的数据拷贝。 | 实现 `MapArea::copy_data` 方法。 | 确保了逻辑段的数据拷贝。 | +| 如何实现单个虚拟页面的映射? | 需要实现单个虚拟页面的映射。 | 实现 `MapArea::map_one` 方法。 | 确保了单个虚拟页面的正确映射。 | +| 如何实现单个虚拟页面的解映射? | 需要实现单个虚拟页面的解映射。 | 实现 `MapArea::unmap_one` 方法。 | 确保了单个虚拟页面的正确解映射。 | +| 如何实现内核地址空间? | 需要实现内核地址空间。 | 定义 `MemorySet::new_kernel` 方法。 | 确保了内核地址空间的创建。 | +| 如何映射内核的跳板? | 需要映射内核的跳板。 | 实现 `MemorySet::map_trampoline` 方法。 | 确保了跳板的正确映射。 | +| 如何映射内核的各个逻辑段? | 需要映射内核的各个逻辑段。 | 在 `MemorySet::new_kernel` 方法中调用 `push` 方法。 | 确保了各个逻辑段的正确映射。 | +| 如何处理内核栈? | 需要处理内核栈。 | 在 `MemorySet::new_kernel` 方法中设置内核栈。 | 确保了内核栈的正确设置。 | +| 如何设置保护页面? | 需要设置保护页面。 | 在内核栈之间预留一个保护页面。 | 确保了内核栈的安全性。 | +| 如何实现恒等映射? | 需要实现恒等映射。 | 使用 `MapType::Identical` 映射方式。 | 确保了内核数据段的正确访问。 | +| 如何设置逻辑段的权限? | 需要设置逻辑段的权限。 | 使用 `MapPermission` 设置权限。 | 确保了逻辑段的权限控制。 | +| 如何创建应用地址空间? | 需要创建应用地址空间。 | 实现 `MemorySet::from_elf` 方法。 | 确保了应用地址空间的创建。 | +| 如何解析 ELF 格式数据? | 需要解析 ELF 格式数据。 | 使用 `xmas_elf` crate 解析 ELF 数据。 | 确保了 ELF 数据的正确解析。 | +| 如何映射跳板? | 需要映射跳板。 | 在 `MemorySet::from_elf` 方法中调用 `map_trampoline` 方法。 | 确保了跳板的正确映射。 | +| 如何映射应用的各个逻辑段? | 需要映射应用的各个逻辑段。 | 在 `MemorySet::from_elf` 方法中调用 `push` 方法。 | 确保了各个逻辑段的正确映射。 | +| 如何处理用户栈? | 需要处理用户栈。 | 在 `MemorySet::from_elf` 方法中设置用户栈。 | 确保了用户栈的正确设置。 | +| 如何设置保护页面? | 需要设置保护页面。 | 在用户栈下方预留一个保护页面。 | 确保了用户栈的安全性。 | +| 如何映射 Trap 上下文? | 需要映射 Trap 上下文。 | 在 `MemorySet::from_elf` 方法中映射 Trap 上下文。 | 确保了 Trap 上下文的正确映射。 | +| 如何返回用户栈顶地址和入口点地址? | 需要返回用户栈顶地址和入口点地址。 | 在 `MemorySet::from_elf` 方法中返回这些地址。 | 确保了这些地址的正确返回。 | +| 如何平滑地从物理地址直接访问过渡到分页模式下的虚拟地址访问 | 在开启分页模式后,CPU 访问内存的方式发生了变化,需要确保切换前后地址空间的连续性 | 在切换satp的指令附近保持地址空间映射的连续性,如保持恒等映射 | 成功实现了从物理地址直接访问到虚拟地址访问的平滑过渡,确保了CPU指令的连续执行不受影响 | +| 如何处理多级页表带来的性能下降问题 | 多级页表增加了MMU的访存次数,加大了多级缓存的压力,导致性能开销增大 | 引入快表(TLB)存储虚拟页号到页表项的映射,减少MMU的访存次数;在切换地址空间时清空快表以保证映射关系的正确性 | 有效缓解了多级页表带来的性能问题,提高了系统的整体效率 | +| 在启用分页机制后,如何高效地在Trap处理时完成地址空间的切换 | 启用分页机制后,Trap处理不仅需要切换栈,还需要切换地址空间,这增加了操作的复杂性 | 使用跳板机制,将应用的Trap上下文存放在应用地址空间的次高页面,避免了在保存Trap上下文前切换到内核地址空间 | 实现了Trap处理时地址空间的高效切换,简化了操作步骤,同时保证了地址空间的安全隔离 | +| 如何解决每个应用地址空间都映射内核段带来的内存占用和安全问题 | 每个应用地址空间都映射内核段会导致额外的内存占用,并且存在安全风险,如熔断漏洞 | 采用内核与应用地址空间隔离的设计,每个应用有自己的地址空间,内核有独立的地址空间 | 显著减少了内存占用,提升了系统的安全性,同时也能更好地支持任务的并发执行 | +| 如何高效地在 Trap 处理时保存和恢复上下文 | Trap 处理不仅需要保存和恢复寄存器状态,还需要切换地址空间,传统的 Trap 处理方法在启用分页机制后变得复杂 | 在 Trap 上下文中增加额外的信息:`kernel_satp`(内核地址空间的 token)、`kernel_sp`(应用内核栈顶的虚拟地址)、`trap_handler`(trap handler 入口点的虚拟地址)。这些信息在应用初始化时由内核写入,之后不再修改 | 提高了 Trap 处理的效率,简化了地址空间切换的过程,确保了系统的稳定性和安全性 | +| 如何确保 Trap 处理时能够平滑地切换地址空间 | 在 Trap 处理过程中,需要从用户地址空间切换到内核地址空间,确保指令能够连续执行 | 使用跳板页面(Trampoline Page),将汇编代码放置在 `.text.trampoline` 段,并对齐到代码段的一个页面中。跳板页面在内核和应用地址空间中均映射到同一物理页帧 | 实现了平滑的地址空间切换,确保了 Trap 处理时指令的连续执行,提高了系统的整体性能 | +| 为什么在 __alltraps 中需要使用 `jr` 而不是 `call` | 在内存布局中,跳转指令和 trap_handler 都在代码段内,但实际执行时的虚拟地址与编译时设置的地址不同 | 使用 `jr` 指令通过寄存器跳转到 trap_handler 入口点,避免了地址偏移量计算错误的问题 | 确保了跳转指令的正确性,避免了因地址偏移量计算错误导致的程序崩溃 | +| 如何管理任务以确保应用的安全隔离 | 为了使应用在运行时有一个安全隔离且符合编译器给应用设定的地址空间布局,操作系统需要对任务进行更多的管理 | 扩展任务控制块(Task Control Block),包含应用的地址空间 `memory_set`、Trap 上下文的物理页号 `trap_cx_ppn` 和应用数据的大小 `base_size` | 提高了任务管理的灵活性和安全性,确保了应用在运行时的安全隔离,支持了更多复杂的应用场景 | +| 应用程序的地址空间与内核地址空间分离,导致 Trap 处理变得复杂 | 内核和应用程序运行在不同的地址空间中,这使得 Trap 上下文的处理需要特别的机制来确保正确性 | 通过在 `task_control_block` 中为每个应用分配独立的内核栈,并在 `trap_handler` 中动态获取当前应用的 Trap 上下文 | 实现了更灵活的任务管理和更安全的 Trap 处理机制,每个应用都能在自己的地址空间中独立运行 | +| 应用程序的 Trap 上下文不在内核地址空间中,无法直接访问 | 应用程序的 Trap 上下文存储在用户空间,而 Trap 处理发生在内核空间 | 使用 `current_trap_cx` 函数获取当前应用的 Trap 上下文的可变引用 | 保证了 Trap 处理的高效性和安全性,同时简化了代码逻辑 | +| 从 S 模式到 S 模式的 Trap 没有合适的处理方式 | 当前系统设计主要关注 U 模式到 S 模式的 Trap,对于 S 模式到 S 模式的 Trap 缺乏考虑 | 在 `trap_handler` 开头调用 `set_kernel_trap_entry` 函数,将 `stvec` 设置为 `trap_from_kernel` 的地址 | 对于 S 模式到 S 模式的 Trap,采取直接 panic 的策略,避免了复杂的上下文保存和恢复过程 | +| 应用程序在从 Trap 返回用户态时需要正确设置 `stvec` | 为了确保应用程序能够正确地 Trap 回到内核,需要在返回用户态前设置正确的 `stvec` 值 | 在 `trap_return` 函数中调用 `set_user_trap_entry` 函数,将 `stvec` 设置为 `TRAMPOLINE` 地址 | 保证了应用程序能够通过 `__alltraps` 正确地 Trap 回到内核,提高了系统的健壮性 | +| 指令缓存可能包含已过时的代码或数据,影响应用程序的正确执行 | 内核中的一些操作可能导致物理页帧内容的变化,而指令缓存在这些变化后可能仍然保留旧的快照 | 在 `trap_return` 函数中使用 `fence.i` 指令清空指令缓存 | 确保了每次从 Trap 返回用户态时,应用程序能够基于最新的代码和数据执行,增强了系统的稳定性和可靠性 | +| 内核无法直接访问应用空间的数据 | 内核与应用地址空间隔离导致直接访问失败 | 使用 `translated_byte_buffer` 函数将应用空间的缓冲区转换为内核可直接访问的形式 | 实现了安全有效的数据访问,避免了内存访问违规 | +| `sys_write` 需要支持不同文件描述符 | 当前实现仅支持标准输出,限制了功能扩展性 | 在 `sys_write` 中增加对其他文件描述符的支持,例如通过查找文件描述符对应的文件对象来确定写入目标 | 扩展了系统调用的功能,使其能适应更多使用场景,如文件写入等 | +| 字节数组切片转字符串可能失败 | 非UTF-8编码的字节序列可能导致转换错误 | 使用 `.unwrap()` 强制转换(虽然不推荐,但在本例中假设输入总是合法的),或更安全地处理转换错误 | 确保了输出的正确性和程序的健壮性,即使遇到非UTF-8编码的数据也能优雅地处理 | + + +#### 练习解题思路 +- 实现 `mmap` + - 根据文档可以描述应用态的虚拟内存管理主要是由 `memory_set`来完成 + - 由此可以直接将在 `TaskControlBlock` 中保存的 `memory_set` 对象进行修改,添加一个 `TaskControlBlock::mmap` 方法 + - 注意需要检查对齐到页,权限校验,与冲突校验 +- 实现 `munmap` + - 和`mmap`类似实现类似,只需要修改 `TaskControlBlock` 中的 `memory_set`对象 + - 注意清除前需要检查虚拟页是否有效 +- 迁移 `sys_get_time` + - 在之前 `sys_get_time` 在用户态和内核态都在同一个大栈内,得到结果后可以直接写入用户传递进来的指针哪里 + - 而现在由于内核和用户的地址空间被用跳板分离,并且每一个应用都使用虚拟内存,传入函数的也是虚拟内存地址,因此需要将虚拟地址转换为物理地址,再写入用户空间 + - 使用 `translated_byte_buffer` 实现一个 `copy_to_user` 函数用作用户态和内核态的转换 +- 迁移 `sys_get_task_info` + - 和`sys_get_time`一样,需要将虚拟地址转换为物理地址,再写入用户空间 + +---- + +### ch5 + + +#### 问题与对应解决操作如下: + +>本章深入探讨了操作系统中进程及进程管理的核心概念,包括关键系统调用(如fork, exec, waitpid)、进程管理的数据结构、进程调度机制以及进程资源回收等,并通过编程作业加深理解。 + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|----------------------------------|------------------------------------------------|-------------------------------------------------------------------------------------|----------------------------------------------------| +| 子进程如何知道自己是新创建的? | 当使用 `fork()` 系统调用创建子进程时,子进程需要知道自己的身份以便执行相应的操作。 | `fork()` 在子进程中返回 0,而在父进程中返回子进程的 PID。 | 子进程可以根据返回值 0 来识别自己是新创建的,而父进程可以使用返回的 PID 来跟踪和管理子进程。 | +| 如何让子进程执行新的程序? | 子进程创建后,默认只是父进程的一个副本,可能需要执行完全不同的程序。 | 使用 `exec()` 系统调用,它可以替换当前进程的地址空间,加载并运行指定的新程序。 | 子进程能够执行全新的程序,实现了进程间的程序切换。 | +| 父进程如何得知子进程的状态变化? | 父进程需要知道子进程何时结束或停止,以便进行资源回收或采取其他行动。 | 通过 `waitpid()` 系统调用,父进程可以等待某个特定的子进程或任何子进程结束,并获取其退出状态。 | 父进程能够有效地管理子进程的生命周期,确保系统资源得到合理利用。 | +| 用户如何与 shell 交互输入命令? | Shell 程序需要能够接收用户的键盘输入,以解析和执行命令。 | 使用 `sys_read()` 系统调用来从标准输入读取用户输入,通常通过封装函数如 `getchar()` 来简化操作。 | 用户可以通过键盘与 shell 交互,输入命令来启动程序或执行其他操作。 | +| 如何优雅地处理用户输入的退格和删除操作? | 用户在输入命令时可能会犯错,需要能够方便地修改已输入的内容。 | Shell 程序检测到退格或删除键时,清除屏幕上的最后一个字符,并从内存中的输入缓存中移除该字符。 | 提供了更好的用户体验,让用户能够更加灵活地编辑他们的命令行输入。 | +| 应用程序只能通过编号加载 | 开发者希望根据应用名称而不是编号来加载应用程序,以提高灵活性和用户体验 | 在链接器脚本中添加应用名称,并在加载器中实现通过名称查找ELF数据的功能 | 用户可以通过应用名称启动应用,提高了系统的易用性和灵活性 | +| 进程标识符分配不高效 | 随着系统中进程数量的增加,简单的线性分配方式可能导致PID耗尽或浪费 | 引入了PidAllocator类,采用栈式分配策略,支持PID的回收和重用 | 系统能够更高效地管理和复用进程标识符,避免了资源浪费 | +| 内核栈管理缺乏自动化机制 | 手动管理内核栈容易出错,且增加了内核开发的复杂度 | 使用RAII模式,通过Drop特性自动管理内核栈的生命周期 | 内核栈的管理变得更加安全可靠,减少了开发者的工作负担 | +| 任务控制块的设计不满足多任务管理的需求 | 原有的任务控制块设计过于简单,无法支持复杂的进程管理功能 | 扩展任务控制块,引入父子进程关系管理,增强进程间通信能力 | 系统能够更好地支持多任务处理,增强了进程间的交互性和稳定性 | +| 任务管理器职责过重 | 任务管理器不仅负责任务调度,还承担了CPU状态的监控,导致模块间耦合度过高 | 将CPU监控功能分离至Processor结构中,专注于任务队列的管理 | 任务管理器变得更加轻量化,模块间的职责划分更加清晰,提高了系统的可维护性和扩展性 | +| 无法准确追踪当前执行的任务 | 在多任务环境中,内核需要随时知道当前哪个任务正在执行,以便进行有效的调度和管理 | 引入 `Processor` 结构,包含 `current` 字段来跟踪当前执行的任务 | 内核能够实时了解当前执行的任务,提高了任务调度的效率和准确性 | +| 缺乏有效的任务切换机制 | 当前任务需要交出CPU使用权时,缺少一种平滑过渡到下一个任务的方法 | 实现 `schedule` 函数,通过 `__switch` 进行任务上下文切换,确保任务间的平滑过渡 | 任务切换更加流畅,减少了上下文切换带来的性能损失 | +| 缺少空闲控制流来处理无任务执行的情况 | 当所有任务都处于阻塞状态时,CPU需要有一个默认的行为来避免空转 | 在 `Processor` 中引入 `idle_task_cx`,并在 `run_tasks` 中实现空闲控制流 | CPU在无任务执行时不会空转,提高了系统的整体效率和稳定性 | +| 初始进程无法启动 | 没有正确的进程控制块 | 使用 `lazy_static` 在运行时动态创建 `INITPROC` 控制块,并通过 `add_task` 将其加入任务管理器 | 成功创建并启动了第一个进程,为后续进程管理打下了基础 | +| 进程调度不准确 | 缺少有效的调度机制 | 实现 `suspend_current_and_run_next` 函数,用于暂停当前任务并选择下一个任务执行 | 系统能够根据需要合理地调度进程,提高了系统的响应性和效率 | +| 子进程地址空间与父进程不同步 | 直接复制父进程的地址空间可能导致数据不一致 | 使用 `MemorySet::from_existed_user` 方法深拷贝父进程的地址空间,确保子进程拥有独立但相同的数据副本 | 子进程能够拥有与父进程相同的应用环境,支持真正的 fork 功能 | +| 父子进程无法正确区分 | 系统调用返回值相同导致混淆 | 修改子进程的 `trap_cx.x[10]` 为 0,表示子进程的 PID,而父进程则返回新创建的子进程的 PID | 确保每次调用 `sys_fork` 时,父进程和子进程都能正确识别自己的身份 | +| 进程退出后资源未回收 | 系统未能及时回收已退出进程的资源 | 实现 `sys_exit` 和 `sys_waitpid` 系统调用,允许父进程等待并收集子进程的退出信息 | 系统资源得到有效管理和利用,避免了僵尸进程的产生 | +| 用户程序如何执行新的程序? | 用户程序想要执行另一个程序时,需要一个方法来加载新程序并开始执行。 | 通过 `sys_exec` 系统调用,内核接收新程序的路径,加载相应的 ELF 文件,创建新的内存布局,并更新 Trap 上下文以指向新程序的入口点。 | 用户程序能够无缝地加载并执行新程序,就像从未离开过一样。 | +| 如何处理 `sys_exec` 调用后 Trap 上下文的变化? | 在 `sys_exec` 调用后,由于地址空间的变化,原有的 Trap 上下文不再有效。 | 在 `trap_handler` 中,在处理完 `sys_exec` 系统调用后再次调用 `current_trap_cx()` 来获取最新的 Trap 上下文。 | 确保 Trap 上下文始终是最新的,从而保证系统调用的正确执行。 | +| 应用程序如何从标准输入读取数据? | 应用程序需要一种方式来读取用户通过键盘输入的数据。 | 实现 `sys_read` 系统调用,使用 SBI 接口 `console_getchar` 读取单个字符,并将其写入用户提供的缓冲区中。 | 应用程序现在可以接收来自用户的直接输入,增强了交互性。 | +| 当应用程序退出或崩溃时,如何妥善处理? | 应用程序可能会主动退出或因错误而崩溃,需要一个机制来清理资源并确保系统的稳定性。 | 在 `sys_exit` 和 `trap_handler` 中调用 `exit_current_and_run_next` 函数,将进程状态设置为僵尸,并清理其资源。 | 退出的应用程序不会留下未清理的资源,系统保持稳定,资源得到有效管理。 | +| 父进程如何回收已退出子进程的资源? | 父进程可能希望知道其子进程何时退出,并回收子进程的资源。 | 实现 `sys_waitpid` 系统调用,允许父进程检查子进程的状态,并在子进程成为僵尸时回收其资源。 | 父进程可以有效地管理其子进程的生命周期,确保资源及时回收,提高系统效率。 | + + + +至此,我们实现了一个基本的进程调度系统,能够支持进程的创建、调度、销毁等基本功能。 + +#### 练习解题思路 +- 实现 `sys_spawn` + 1. **获取 ELF 文件数据**: + - 从系统调用参数中获取 ELF 文件的数据。这通常是一个指向 ELF 文件数据的指针和数据的长度。 + + 2. **创建新的内存集**: + - 使用 `MemorySet::from_elf` 方法从 ELF 文件数据创建一个新的内存集。这将解析 ELF 文件并设置相应的内存段。 + + 3. **分配 PID 和内核栈**: + - 为新进程分配一个新的 PID。 + - 分配一个新的内核栈,并获取其顶部地址。 + + 4. **初始化任务控制块 (TCB)**: + - 创建一个新的 `TaskControlBlock` 结构体,初始化其各个字段,包括内存集、陷阱上下文、任务上下文等。 + - 设置陷阱上下文的内核栈指针和用户栈指针。 + - 设置新进程的入口点。 + + 5. **添加子进程到父进程的子进程列表**: + - 将新创建的子进程添加到父进程的子进程列表中。 + + 6. **返回子进程的 PID**: + - 返回新创建的子进程的 PID 给调用者。 + + +- 实现 `sys_set_priority` 并且实现一个优先级调度算法,例如 `Stride` 调度算法 + - 实现 `sys_set_priority` + + 1. **系统调用接口**: + - 定义 `sys_set_priority` 系统调用,接收两个参数:进程的 PID 和新的优先级值。 + + 2. **查找目标进程**: + - 根据传入的 PID 查找对应的 `TaskControlBlock`(TCB)实例。 + + 3. **更新优先级**: + - 更新找到的 TCB 实例中的优先级字段。 + + 4. **更新 Stride 值**: + - 调用 `update_stride` 方法更新该 TCB 的 Stride 值,以反映新的优先级。 + + 5. **返回结果**: + - 如果成功更新优先级,返回 0;否则返回错误码。 + + - 实现 `Stride` 调度算法 + + 1. **定义 Stride 结构**: + - 使用 `Stride` 结构体来表示每个进程的 Stride 值,包含一个 `u64` 类型的字段。 + + 2. **更新 Stride 值**: + - 在 `TaskControlBlock` 中定义 `update_stride` 方法,用于更新 Stride 值。该方法使用公式 `(self.stride.0 + INIT_STRIDE * self.priority as u64) % (BIG_STRIDE + 1)` 来计算新的 Stride 值,防止溢出。 + + 3. **实现比较器**: + - 为 `Stride` 结构体实现 `Ord`、`Eq`、`PartialOrd` 和 `PartialEq` trait,以便在调度器中进行排序和比较。 + - 比较器的逻辑是:计算两个 Stride 值的差值,并根据差值的大小决定顺序。这样可以确保 Stride 值在循环范围内正确比较。 + + 4. **任务管理器**: + - 在任务管理器中维护一个优先级队列(如 `BinaryHeap`),用于存储所有就绪态的 TCB。 + - 每次调度时,从优先级队列中取出 Stride 值最小的 TCB 进行执行。 + - 执行完后,更新该 TCB 的 Stride 值,并重新插入优先级队列。 + +- 迁移 `mmap`, `munmap`, `sys_get_time`, `sys_get_task_info` + - 基本都可以无损实现迁移 + +---- + +### ch6 + +>本章深入探讨了文件系统与I/O重定向的概念,通过介绍文件描述符、文件I/O操作、简易文件系统easy-fs的设计与实现,以及文件系统在内核中的集成,旨在加深读者对操作系统文件处理机制的理解。 + +#### 问题与对应解决操作如下: + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|------------------------------------|-----------------------------------------------------|----------------------------------------------------------------------------------------|--------------------------------------------------| +| 标准输入输出接口不够通用 | 初始实现仅针对标准输入输出,限制了程序的灵活性 | 为标准输入输出实现 `File` Trait,使它们能够像普通文件一样被处理 | 程序可以更灵活地处理各种输入输出流,不再局限于标准输入输出 | +| 文件描述符表固定大小限制了文件数量 | 使用固定大小的文件描述符表,无法适应进程需求的变化 | 引入动态长度的 `Vec` 来存储文件描述符,移除了文件数量的硬性限制 | 进程可以根据实际需要打开任意数量的文件,提升了系统的灵活性和可扩展性 | +| 文件描述符状态管理复杂 | 每个文件描述符都需要单独管理其打开和关闭状态,增加了系统负担 | 使用 `Option` 包装文件描述符,通过 `None` 和 `Some` 区分空闲和占用状态 | 简化了文件描述符的状态管理,降低了实现复杂度,提高了代码的可维护性 | +| 文件共享困难 | 不同进程间难以共享同一文件,限制了多进程协作 | 使用 `Arc` 实现文件的共享引用,允许多个进程同时读写同一文件 | 提升了多进程间文件共享的能力,促进了进程间的协作,增强了系统的交互性 | +| 系统调用仅限于标准输入输出 | 早期的 `sys_read` 和 `sys_write` 系统调用只能用于标准输入输出,限制了功能 | 重构系统调用,使其能够根据文件描述符调用相应的 `File` Trait 方法 | 系统调用变得更加通用,可以应用于任何实现了 `File` Trait 的文件对象 | +| 文件打开失败 | 尝试打开的文件不存在或路径错误 | 使用 `CREATE` 标志尝试打开文件,若文件不存在则创建之 | 成功打开或创建了文件,程序继续执行 | +| 写入数据失败 | 文件描述符无效或磁盘空间不足 | 检查文件描述符是否正确,确保有足够的磁盘空间 | 数据成功写入文件,文件内容更新 | +| 读取数据失败 | 文件为空或读取操作超出文件末尾 | 确认文件中有数据,检查读取长度是否正确 | 正确读取到了文件中的数据,内容显示无误 | +| 关闭文件失败 | 文件描述符已关闭或从未打开 | 在每次操作文件前后检查文件描述符状态 | 文件被正确关闭,资源得以释放 | +| 多次读取时未能正确读取所有数据 | 缓冲区大小不足以一次性读取全部数据,或读取逻辑有误 | 实现循环读取机制,直到读取到文件末尾 | 所有数据都被完整地读取出来,没有遗漏 | +| 文件系统与内核紧密耦合 | 随着内核功能增加,代码量增大,导致文件系统与内核之间的耦合度越来越高,难以单独开发和测试 | 将 easy-fs 分离成独立的 crate,并且通过抽象接口 BlockDevice 连接底层设备驱动 | 实现了文件系统的模块化,提高了代码的可维护性和可测试性 | +| 磁盘IO操作频繁导致性能下降 | 每次读写文件都需要访问磁盘,增加了系统的I/O开销,影响了整体性能 | 引入块缓存层,利用内存作为磁盘的缓存,减少直接磁盘访问 | 显著提高了文件系统的响应速度和整体效率 | +| 缓存替换策略不合理可能导致常用数据被误删 | 当内存中的缓存满了,如果替换策略不当,可能会把频繁使用的数据替换出去,影响性能 | 使用类FIFO的缓存替换算法,优先替换掉最少使用的数据 | 优化了缓存机制,确保了常用数据能够留在内存中,进一步提升了性能 | +| 并发访问同一块缓存可能导致数据不一致 | 在多线程环境中,多个线程同时访问同一块缓存,如果没有适当的同步机制,会导致数据不一致 | 采用 Arc> 结构,提供了共享引用和互斥访问的能力 | 有效防止了并发访问带来的数据竞争问题,保证了数据的一致性和安全性 | +| 缓存数据未及时同步到磁盘可能导致数据丢失 | 如果缓存中的数据被修改后没有及时写回到磁盘,一旦系统崩溃,可能会导致数据丢失 | 在 BlockCache 的 Drop 实现中检查 modified 标记,必要时将数据写回磁盘 | 确保了即使在系统异常退出的情况下,也能尽量减少数据丢失的风险 | +| 文件系统启动时找不到超级块 | 超级块是文件系统的重要组成部分,如果找不到或者损坏了,整个文件系统就无法正常工作 | 设计了一个超级块 `SuperBlock`,包含魔数 `magic` 用于验证文件系统的合法性 | 确保了文件系统启动时可以快速准确地定位超级块,增强了系统的健壮性 | +| 无法高效地管理大量的索引节点和数据块 | 随着文件数量的增加,索引节点和数据块的数量也随之增加,传统的线性查找方式效率低下 | 引入了位图 `Bitmap`,通过位图管理索引节点和数据块的分配状态 | 大幅提升了文件系统管理大量索引节点和数据块的效率,减少了查找时间 | +| 数据块分配过程中可能出现浪费 | 在分配数据块时,如果每次只分配一个块,可能会导致大量的小块分散在磁盘上,造成空间浪费 | 位图 `Bitmap` 通过逐块扫描和按组分配的方式,确保了高效利用磁盘空间 | 减少了磁盘碎片,提高了磁盘空间利用率,使得文件系统更加紧凑 | +| 并发情况下位图操作可能导致数据不一致 | 在多线程或多进程环境下,多个进程同时操作位图可能会导致数据不一致,例如同一个数据块被多次分配 | 使用 `Arc>` 结构,确保了位图操作的原子性和一致性 | 有效防止了并发操作带来的数据竞争问题,保证了文件系统的稳定性和可靠性 | +| 文件读取超出范围时返回0 | 当尝试从文件的某个偏移位置读取数据时,如果该位置已经超过了文件的实际大小,函数将无法读取到任何数据 | 在`read_at`方法中加入边界检查逻辑,确保读取操作不会超出文件的实际大小 | 用户尝试读取不存在的数据时,程序能优雅地处理这种情况,避免了潜在的错误 | +| 目录项名称长度限制 | 为了保证目录项的紧凑性和高效性,每个目录项的文件名长度被限制在27个字符以内 | 通过定义`NAME_LENGTH_LIMIT`常量来控制文件名的最大长度,并在创建目录项时进行长度校验 | 保证了文件系统的稳定性和效率,同时也能适应大多数文件命名的需求 | +| 文件扩容时需要额外分配数据块 | 当文件需要增长时,可能需要额外的数据块来存储新增加的内容 | 实现`increase_size`方法,根据新旧文件大小计算所需的额外数据块数量,并分配这些块 | 文件可以动态增长,用户不必担心文件大小的限制,提高了用户体验 | +| 清空文件时需要回收所有数据块 | 当用户删除文件或清空文件内容时,为了释放磁盘空间,需要回收所有与该文件关联的数据块 | 实现`clear_size`方法,该方法负责追踪并回收所有相关的数据块 | 有效管理了磁盘空间,防止了空间浪费,保持了文件系统的健康状态 | +| 目录项转换为字节序列 | 为了使用`read_at`和`write_at`方法处理目录项,需要将目录项对象转换为字节序列 | 为`DirEntry`结构体实现了`as_bytes`和`as_bytes_mut`方法,允许对象与字节序列之间的转换 | 使得目录项可以方便地被读写,简化了目录操作的实现 | +| 创建 easy-fs 文件系统时,如何确保所有磁盘块都被正确初始化? | 初始状态下,磁盘可能含有旧数据或随机值,这可能导致新创建的文件系统不稳定。 | 在 `create` 方法中,遍历所有磁盘块,并将它们清零。 | 新创建的 easy-fs 文件系统在磁盘上拥有干净的状态,没有残留数据干扰。 | +| 如何从已存在的块设备上加载 easy-fs 文件系统? | 直接读取块设备上的数据无法保证读到的是有效的 easy-fs 文件系统,可能会遇到损坏或不兼容的情况。 | 使用 `open` 方法读取超级块(SuperBlock),验证其有效性后,根据超级块信息构造 EasyFileSystem 实例。 | 成功加载并验证了文件系统,用户可以安全地与之交互。 | +| 如何确定一个 inode 在磁盘上的确切位置? | 文件系统需要快速准确地定位到特定的 inode,以便进行读写等操作。 | 通过 `get_disk_inode_pos` 方法,基于 inode 编号计算出其所在的磁盘块编号及块内的偏移量。 | 提供了一种高效的方法来定位磁盘上的 inode,加速了文件操作。 | +| 文件系统如何管理数据块的分配与回收? | 数据块的有效管理是文件系统性能的关键,错误的分配或未能及时回收会导致空间浪费或文件损坏。 | 实现了 `alloc_data` 和 `dealloc_data` 方法,前者用于分配新的数据块,后者则负责回收不再使用的数据块。 | 确保了数据块资源的合理利用,提高了文件系统的稳定性和效率。 | +| 如何获取根目录的 Inode? | 根目录是文件系统中所有其他文件和目录的起点,获取其 Inode 是进行任何文件操作的前提。 | 通过 `root_inode` 方法,根据已知的根目录 inode 编号(总是0)来创建对应的 Inode 结构。 | 用户能够顺利地从根目录开始浏览和操作文件系统。 | +| 文件索引查找效率低下 | 因为所有的文件都在根目录下,所以在查找文件时需要遍历整个根目录的目录项,导致效率不高 | 实现了一个 `find` 方法,该方法直接在根目录的 `DiskInode` 中根据文件名查找对应的 `inode` 编号,并利用该编号生成新的 `Inode` 对象 | 提高了文件索引的查找速度,用户可以更快地访问到所需文件 | +| 多核环境下文件系统操作冲突 | 当多个核心同时访问文件系统时,可能会出现数据竞争或不一致的问题 | 在所有暴露给用户的文件系统操作中加入了对 `EasyFileSystem` 的互斥锁,确保同一时间只有一个核心可以进行文件系统操作 | 保证了文件系统在多核环境下的稳定性和数据一致性 | +| 文件创建时重复文件检测不足 | 在创建新文件前,如果没有正确检查文件是否已经存在,可能导致重复文件的创建 | 在 `create` 方法中添加了检查逻辑,通过调用 `find_inode_id` 来判断文件是否已存在,如果存在则返回 `None` | 避免了重复文件的创建,提高了文件系统的可靠性 | +| 文件清空时资源回收不彻底 | 清空文件时如果只删除了文件的 `inode` 信息而没有释放其占用的数据块,会导致磁盘空间的浪费 | 在 `clear` 方法中,不仅清除了 `inode` 的大小信息,还回收了之前分配给该文件的所有数据块 | 优化了磁盘空间的使用,避免了不必要的资源浪费 | +| 应用程序打包至文件系统时路径处理不当 | 在将应用程序从主机文件系统复制到 `easy-fs` 时,如果路径处理不当,可能导致文件复制失败或错误 | 使用 `clap` crate 解析命令行参数,明确指定了源文件夹和目标文件夹路径,并在复制文件时进行了正确的路径拼接 | 成功将应用程序打包到 `easy-fs` 中,为后续的应用程序执行提供了便利 | +| 内核如何与块设备通信? | 在操作系统中,直接与硬件通信通常需要特殊的接口,对于块设备也是如此。 | 使用 `virtio-drivers` crate 并在内核中创建一个全局的 `BLOCK_DEVICE` 实例,通过 VirtIO 协议与 QEMU 虚拟化的块设备通信。 | 内核能够高效地读取和写入虚拟硬盘,就像操作真实硬件一样。 | +| 如何在 QEMU 中添加虚拟硬盘? | 默认情况下,QEMU 虚拟机没有附加任何存储设备,需要手动配置。 | 在启动 QEMU 时,通过命令行参数 `-drive` 和 `-device` 添加一个 VirtIO 块设备,并指定其镜像文件和总线连接方式。 | 成功为虚拟机添加了一个可读写的虚拟硬盘,可以用于存放文件系统。 | +| 文件系统如何初始化? | 文件系统需要知道从哪里开始读取数据,以及如何管理文件和目录。 | 在内核启动时,通过 `EasyFileSystem::open` 从块设备打开文件系统,并获取根目录的 `inode`。 | 文件系统准备就绪,可以进行文件的查找、读取和写入等操作。 | +| 如何实现文件的打开功能? | 文件的打开涉及到权限检查、文件是否存在以及是否需要创建新文件等逻辑。 | 定义 `OpenFlags` 枚举来表示不同的打开模式,并实现 `open_file` 函数处理各种情况。 | 用户程序可以通过系统调用 `sys_open` 以不同的模式打开文件,满足多样化的文件操作需求。 | +| 应用程序如何加载和执行? | 程序需要从磁盘加载到内存中,并正确设置进程环境才能执行。 | 修改 `sys_exec` 系统调用,使其从文件系统中读取应用程序的 ELF 数据,并将其加载到新的任务控制块中。 | 应用程序可以从文件系统中加载并成功执行,为用户提供了丰富的应用程序运行环境。 | + + +至此,我们实现了一个简单的文件系统,能够支持文件创建、删除、打开、读取、写入等基本操作。 + + +#### 练习解题思路 + + +- 实现 `linkat` + - 本章中`OsInode`用作权限管理,`Inode`用作操作控制, `DiskInode`则可视为作为内存中的文件镜像,与`BlockCache`一起可以根据需求来选择同步读或者写策略 + - 对于硬链接需要维护一个引用计数,才能决定是否需要删除文件,方便起见直接把引用计数放在`DiskInode`中 + - 此外拥有了引用计数还不足以实现硬链接,因为`DiskInode`中并没有用作身份表示的数据段,所以用户是无法直接分辨引用的哪一个`DiskInode` + - 作为唯一标识符 `inode_id` 在 `Inode` 中被维护,而 `Inode` 会创建 `DiskInode` 所以可以在`DiskInode`的创建阶段中添加一个 `inode_id` 字段,来标识身份 + - 由此便可以实现 `linkat`了,首先获取目标`Inode`,然后将目标`DiskInode`的引用计数加一,同时创建一个指向目标`DiskInode`的 `DirEntry` +- 实现 `unlinkat` + - 获取待删除的`Inode`,将其引用计数减一,若为0则删除,并且同时移除 `DirEntry` +- 实现 `fstat` + - 获取直接对应的`Inode`,返回其元数据 +- 迁移 `Stride`&&`sys_set_priority`, `sys_spawn` , `mmap`, `munmap`, `sys_get_time`, `sys_get_task_info` + - 基本无损迁移 + +---- + +### ch7 + +>本章详细介绍了进程间通信的基本概念、管道的使用方法及其系统调用、命令行参数处理以及标准I/O重定向等核心内容。 + + +#### 问题与对应解决操作如下: + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|--------------------|---------------------------------------------------|---------------------------------------------------------------------------------------------------------|-----------------------------------------| +| 如何创建管道? | 需要在进程间建立一种简单的单向通信机制,以传递数据。 | 使用 `sys_pipe` 系统调用,为当前进程创建一个管道,其中包括一个只读文件描述符和一个只写文件描述符。 | 成功创建了管道,为进程间的通信提供了基础。 | +| 管道如何处理读写操作? | 当数据从一个进程写入管道后,另一个进程需要能够正确读取这些数据,且读写操作不应导致数据丢失或混乱。 | 通过实现 `File` Trait 的 `read` 和 `write` 方法,确保管道能够安全地进行读写操作,同时利用循环队列管理数据流,保证数据的完整性和顺序。 | 管道实现了可靠的进程间数据传输,避免了数据乱序或丢失。 | +| 如何关闭管道? | 当不再需要管道时,应当释放相关资源,避免内存泄漏。 | 使用 `sys_close` 系统调用关闭不再使用的管道端口。当一个管道的所有读端或写端都被关闭后,管道会自动回收资源。 | 管道资源得到了有效管理,避免了不必要的资源浪费。 | +| 如何检测管道的写端是否全部关闭? | 在读取管道时,如果所有写端都已经关闭,应该能够立即得知,而不是无限期等待。 | 在 `PipeRingBuffer` 中实现 `all_write_ends_closed` 方法,通过检查写端的弱引用计数是否能升级为强引用计数来判断所有写端是否已关闭。 | 读端可以及时了解写端的状态,避免无效等待,提高了程序的响应性。 | +| 如何处理管道中的阻塞读取? | 读取管道时,如果当前没有数据可读,读操作可能会阻塞,直到有新的数据写入。 | 在 `Pipe` 的 `read` 方法中,如果当前管道中没有可读数据且所有写端未关闭,则通过 `suspend_current_and_run_next` 暂停当前任务,等待其他任务写入数据后再继续读取。 | 读操作能够正确地等待新数据的到来,而不会浪费CPU资源,提高了系统的整体效率。 | +| 命令行参数无法正确传递给应用 | 在早期版本中,`sys_exec` 系统调用仅接受路径参数,缺少传递命令行参数的机制 | 修改 `sys_exec` 接口,增加一个 `args` 参数来传递命令行参数数组 | 开发者可以轻松地在自己的应用中利用命令行参数,增强了应用的灵活性 | +| 用户栈上的命令行参数格式不正确 | 当命令行参数被压入用户栈时,没有正确处理字符串的终止符 `\0` | 在压入每个命令行参数时,确保在其末尾添加 `\0` 符号 | 应用在启动时能够正确解析命令行参数,避免了因字符串未正确终止导致的问题 | +| 标准输入输出重定向功能缺失 | 应用默认使用标准输入输出,缺乏对文件重定向的支持 | 引入 `sys_dup` 系统调用,允许文件描述符的复制与替换;在 shell 程序中处理 `<` 和 `>` 符号,实现输入输出重定向 | 用户可以通过简单的命令行语法,灵活地将文件作为应用的输入或输出,提升了用户体验 | +| 输入输出重定向时文件描述符管理不当 | 在执行输入输出重定向时,直接关闭标准输入输出文件描述符,可能导致资源泄露 | 使用 `close` 关闭不再需要的文件描述符,使用 `dup` 进行文件描述符的复制,确保资源正确回收 | 文件描述符得到有效管理,避免了资源泄露,提高了系统的稳定性和安全性 | +| 命令行参数在用户态应用中无法正确恢复 | 应用在启动时接收到了命令行参数的个数和指针数组的基地址,但没有有效的方法将其转换为可使用的格式 | 在用户态应用的 `_start` 函数中,根据命令行参数的个数和指针数组的基地址,逐个恢复命令行参数为 `&str` 类型 | 应用开发者可以方便地在主函数中直接使用命令行参数,提高了开发效率 | + +#### 练习解题思路 +- undefined + +---- + + +### ch8 + +>主要探讨了并发程序设计,包括线程的概念、管理、同步互斥机制(如锁、信号量和条件变量),以及相关系统调用的实现,旨在通过理论与实践结合的方式,帮助读者深入理解并掌握多线程编程及其在操作系统层面的支持。 + + +#### 问题与对应解决操作如下: + +| 问题 | 原因 | 解决方案 | 最终效果 | +|-------------------------|-------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|--------------------------------------------------------------| +| 线程创建后的等待机制不够高效 | 当主线程调用 `sys_waittid` 检查子线程是否已经退出时,如果子线程还没有退出,主线程就会不断地尝试检查,这会导致CPU资源的浪费。 | 引入 `yield_()` 函数,在检测到目标线程未退出时让出CPU使用权,等到下一次调度时再进行检查。 | 这样做不仅节省了CPU资源,还提高了系统的整体效率,就像学会了“休息也是战斗力”的道理一样。 | +| 线程与进程的数据结构分离导致的复杂性增加 | 随着线程概念的引入,原本属于进程的一些属性需要被独立出来,形成新的线程控制块,这增加了系统的复杂度。 | 将线程特有的信息封装进 `TaskControlBlock` 结构中,并在 `ProcessControlBlock` 中添加了一个线程控制块向量,用于管理同一个进程下的多个线程。 | 通过这种巧妙的设计,不仅解决了线程管理的问题,还保持了系统的清晰和模块化,就像给混乱的书架找到了整理的方法。 | +| 多线程程序中线程退出后的清理工作 | 在多线程环境中,当某个线程完成任务并退出后,需要有一个机制来清理该线程占用的资源,确保不会造成内存泄漏。 | 在线程控制块中加入了 `exit_code` 字段来记录线程退出的状态,并且在主线程中通过 `waittid` 来回收已退出线程的资源。 | 这个解决方案保证了资源的有效管理和回收,就像给花园定期除草一样,让系统环境更加健康。 | +| 线程调度的公平性和效率 | 在多线程环境下,如何确保各个线程能够公平地获得CPU时间片,同时又不失调度的效率,是一个重要的问题。 | 利用 `Processor` 结构来管理线程的调度,通过合理的算法确保每个线程都有机会执行,同时也考虑到了高优先级线程的需求。 | 这样的调度策略既保证了公平性,也提高了系统的响应速度和吞吐量,就像是找到了团队合作的最佳模式,每个人都能发挥自己的长处。 | +| 如何创建一个新线程? | 需要扩展进程的能力,使其能够同时执行多个任务。 | 使用 `sys_thread_create` 系统调用,创建新的 `TaskControlBlock`,并将其添加到调度队列中。 | 成功地在现有进程中启动了一个新线程,增加了系统的并发处理能力。 | +| 线程退出时如何不影响其他线程? | 非主线程退出时,不应该影响到同一进程内的其他线程。 | 实现 `sys_exit` 系统调用,只回收当前线程的资源,不影响其他线程。如果退出的是主线程,则整个进程及所有线程一起退出。 | 保证了线程间相互独立,提高了系统的稳定性和灵活性。 | +| 如何优雅地等待线程结束? | 主线程可能需要等待某个子线程完成特定的任务。 | 提供 `sys_waittid` 系统调用,允许主线程检查子线程是否已退出,并获取其退出状态。 | 使主线程能够有效地管理子线程,确保任务按预期顺序完成。 | +| 线程之间的资源如何有效管理? | 创建和销毁线程时,需要确保资源的合理分配和回收。 | 在创建线程时分配必要的资源(如栈空间),在线程退出时回收这些资源。 | 资源得到了高效利用,减少了内存泄漏的风险。 | +| 如何实现线程间的特权级切换? | 线程在执行过程中可能需要从用户态切换到内核态,以执行某些特权操作。 | 利用硬件支持的特权级机制,以及在创建线程时准备好的跳板页,确保线程可以安全地进行用户态与内核态的切换。 | 保障了系统的安全性,同时实现了高效的线程执行。 | +| 多线程同时访问共享数据会导致错误结果 | 操作系统在调度时可能会在关键操作中途切换线程,导致数据状态不一致。 | 引入锁机制,确保同一时间只有一个线程可以访问共享数据。 | 保证了共享数据的一致性和完整性,避免了竞态条件。 | +| 线程在执行临界区代码时被中断 | 线程在执行多条汇编指令时,可能被操作系统中断,导致数据不一致。 | 使用锁来保护临界区代码,确保线程在执行完所有相关指令前不会被中断。 | 确保了临界区代码的原子性,避免了数据竞争。 | +| 如何选择合适的锁机制 | 忙等锁和睡眠锁各有优缺点,选择不当会影响性能或公平性。 | 根据应用场景选择合适的锁机制,例如使用睡眠锁减少CPU浪费。 | 提高了系统的整体性能和响应速度,同时保证了公平性。 | +| 如何实现可睡眠锁 | 需要操作系统支持,让等待锁的线程睡眠,释放锁的线程唤醒等待的线程。 | 使用 `mutex_lock` 和 `mutex_unlock` 系统调用来管理锁的状态和等待队列。 | 实现了高效的锁管理,减少了不必要的CPU消耗。 | +| 如何处理多个线程竞争锁的情况 | 当多个线程同时竞争同一个锁时,需要确保公平性和性能。 | 使用等待队列来管理等待锁的线程,确保公平性,并在释放锁时唤醒最早等待的线程。 | 保证了锁的竞争公平性,提高了系统的并发处理能力。 | +| 互斥锁不能处理多个线程同时访问资源的问题 | 互斥锁只能让一个线程独占资源,无法满足多线程并发的需求 | 引入信号量机制,允许设定最大并发线程数N | 实现了更加灵活的资源访问控制,提高了并发效率 | +| 信号量操作需要保证原子性以防止竞态条件 | 在进行信号量的增减操作时,如果不保证操作的原子性,可能会导致数据不一致 | 使用原子指令或互斥锁保护信号量的读写操作 | 确保了信号量操作的安全性,避免了竞态条件的发生 | +| 信号量的初始值设置不当可能导致死锁 | 如果信号量的初始值设得过小,可能会导致线程因为无法获得资源而长时间等待 | 根据实际情况合理设置信号量的初始值 | 避免了不必要的线程等待,减少了死锁的风险 | +| 信号量用于同步时,线程唤醒顺序不确定 | 在多个线程等待同一信号量时,V操作可能随机唤醒其中一个线程,导致执行顺序混乱 | 通过FIFO或其他策略管理等待队列,确保按顺序唤醒线程 | 保证了线程间的有序执行,增强了程序的可预测性和稳定性 | +| 信号量资源未及时释放可能引起资源泄露 | 如果线程在完成任务后忘记释放持有的信号量,会导致其他线程永远无法获取到资源 | 在线程退出前确保调用V操作释放信号量 | 防止了资源泄露,保证了系统资源的有效利用 | +| 线程 second 会忙等检查,浪费处理器时间 | 使用互斥锁和简单的循环检查条件会导致 CPU 时间浪费 | 引入条件变量,让线程 second 在条件不满足时休眠 | 提高了 CPU 利用率,减少了不必要的等待 | +| 线程 second 休眠时持有互斥锁,导致死锁 | 线程 second 在等待条件时没有释放互斥锁,使得其他线程无法获得锁来改变条件 | 在条件变量的 wait 操作中先释放互斥锁,再将线程挂起 | 避免了死锁的发生,确保了线程间正确的同步 | +| 线程之间的唤醒和等待缺乏有效的通信机制 | 单纯的互斥锁和信号量不足以表达复杂的线程间通信需求 | 实现条件变量的 wait 和 signal 操作,提供线程间等待和唤醒的机制 | 增强了线程间的协作能力,使并发编程更加灵活 | +| 条件变量的唤醒操作可能没有线程在等待 | 有时唤醒操作发生在没有线程等待的情况下,这会导致资源浪费 | 优化条件变量的实现,确保只有在有线程等待时才执行唤醒操作 | 提升了系统的响应性和资源利用率 | +| 不同的唤醒行为语义可能导致程序行为不一致 | Hoare、Hansen 和 Mesa 三种语义的选择会影响线程的唤醒和执行顺序 | 选择合适的语义(例如 Mesa 语义)并明确文档化,确保开发者理解行为 | 减少了由于语义不清导致的编程错误,提高了代码的可维护性 | + + +至此,我们又有了一些并发支持 + + +#### 练习解题思路 + +- 实现 `sys_enable_deadlock_detect` + - 检测`Mutex`死锁 + - 死锁发生是由于存在等待环,可以通过检测是否存在等待环来确定是否会发生死锁 + - 而检测环的时候,如果 `Mutex` 为一个节点,那么我们就需要知道它的入读和出度,由此可向 `Mutex` 额外添加`wait_queue`表示入度,`holder`表示出度 + - 如果存在环,那么就存在死锁 + - 检测Semaphore死锁 + - 类似于`Mutex`,同样可以检测是否存在等待环,不过这里`holders`表示出度 +- 无迁移要求XD + + +---- + +# 总结 + +断断续续写了一个月,途中可谓受尽苦难,最后才把所有东西都提交上去,希望自己能坚持下去,不过收获良多,这里感谢提供这么棒的平台,也感谢老师! + +ch3的题目 +![img.png](2024A-OS-II-Summary/img.png) + + +ch8的题目 +![img_1.png](2024A-OS-II-Summary/img_1.png) diff --git a/source/_posts/2024A-OS-II-Summary/img.png b/source/_posts/2024A-OS-II-Summary/img.png new file mode 100644 index 00000000000..2e675c08859 Binary files /dev/null and b/source/_posts/2024A-OS-II-Summary/img.png differ diff --git a/source/_posts/2024A-OS-II-Summary/img_1.png b/source/_posts/2024A-OS-II-Summary/img_1.png new file mode 100644 index 00000000000..3fdf4b7875c Binary files /dev/null and b/source/_posts/2024A-OS-II-Summary/img_1.png differ diff --git a/source/_posts/2024A-rcore-camp-stag2-moyigeek.md b/source/_posts/2024A-rcore-camp-stag2-moyigeek.md new file mode 100644 index 00000000000..71cef29adf6 --- /dev/null +++ b/source/_posts/2024A-rcore-camp-stag2-moyigeek.md @@ -0,0 +1,61 @@ +--- +layout: npm +title: 2024A-rcore-camp-stag2-moyigeek +date: 2024-11-11 15:18:24 +mathjax: true +tags: + - author:moyigeek + - repo:https://github.com/LearningOS/2024a-rcore-moyihust +--- + +## moyigeek 的2024秋冬rcore训练营阶段二笔记 + + +## lab1 +在TCB中加入start_time和syscall_times字段,并在trap中更新syscall_times,在taskManager的run_first_task和run_next_task中维护start_time字段,最后在syscall中通过get_current_task_control_block实现返回task_info + +## lab2 +使用translated_byte_buffer来实现结构体完整性以完成sys_get_time 和 sys_task_info.然后完成申请内存的检查以及申请的释放。 + + +## lab3 +在本次实验中,我们将当前运行程序的处理从 `taskmanager` 中分离出来,交由 `processor` 处理,并使用修改后的接口完成 `ch3-4` 的系统调用。我们仿照 `exec` 和 `fork` 实现了 `spawn` 流程,并创建了一个新的地址空间。此外,我们在 PCB 中添加了 `priority` 和 `stride` 字段,并在选择时遍历选择 `stride` 最小的进程。 + +在这个实验中,认识了完整的PCB结构,了解了进程的创建和调度,了解了进程的调度算法,了解了进程的调度流程,了解了进程的调度接口。 + +{% asset_img PCB.png PCB %} + +关于其中stride属性的溢出问题 +可以看到由于溢出的出现,进程间stride的理论比较和实际比较结果出现了偏差。我们首先在理论上分析这个问题:令PASS_MAX为当前所有进程里最大的步进值。则我们可以证明如下结论:对每次Stride调度器的调度步骤中,有其最大的步进值STRIDE_MAX和最小的步进值STRIDE_MIN之差: + +$STRIDE_{MAX} – STRIDE_{MIN} <= PASS_{MAX}$ + +在溢出情况下,P.pass_B 可能会变得很小,而 P.pass_A 仍然很大。 +由于模运算的性质,(P.pass_B + MAX_VALUE) - P.pass_A 仍然是一个有效的差值。 +因此,即使发生溢出,STRIDE_{MAX} - STRIDE_{MIN} <= PASS_{MAX} 仍然成立,因为 PASS_{MAX} 是所有进程中最大的步进值。 + + +## lab4 +在easy-fs中添加硬链接的功能,在inode中添加全局的nlink bitmap来保存链接的个数,由此实现sys_stat.通过在easy_fs的功能,在os中实现创建链接和删除链接的系统调用。 + +这章实验的复杂度较高,额外挂载了一个文件系统easy-file-system以及新增了虚拟文件系统的接口。实现这一章的重点在于理解磁盘管理的操作以及文件系统的架构,同时也要理解Rust如何实现以极低的耦合实现虚拟文件系统。 +其中文件系统的结构如图: + +{% asset_img file.jpg file_system %} + +1. 磁盘块设备接口层:以块为单位对磁盘块设备进行读写的 trait 接口 +`BlockDevice` +2. 块缓存层:在内存中缓存磁盘块的数据,避免频繁读写磁盘 +`BlockCache` +3. 磁盘数据结构层:磁盘上的超级块、位图、索引节点、数据块、目录项等核心数据结构和相关处理 +磁盘数据结构层的代码在 layout.rs 和 bitmap.rs 中。 +4. 磁盘块管理器层:合并了上述核心数据结构和磁盘布局所形成的磁盘文件系统数据结构 +5. 索引节点层:管理索引节点,实现了文件创建/文件打开/文件读写等成员函数 + + +## lab5 +实现了死锁检测,创建了 `DeadlockDetect` 结构体,并实现了银行家算法。在系统调用中初始化系统资源和进程资源,实现了资源的申请和释放,并实现了死锁的检测。在是实现中使用渐近的银行家算法在创建资源的时候初始化资源的数量,并在申请的时候检查是否会造成死锁。 + + +## 总结 +在这次的训练营中,我学到了很多关于操作系统的知识,包括进程管理,文件系统,内存管理,死锁检测等等。在这个过程中,我也学到了很多关于Rust的知识,包括Rust的内存管理,Rust的并发模型等等。这次训练营让我对操作系统有了更深的理解,也让我对Rust有了更深的理解。 \ No newline at end of file diff --git a/source/_posts/2024A-rcore-camp-stag2-moyigeek/PCB.png b/source/_posts/2024A-rcore-camp-stag2-moyigeek/PCB.png new file mode 100644 index 00000000000..25607fdcb3e Binary files /dev/null and b/source/_posts/2024A-rcore-camp-stag2-moyigeek/PCB.png differ diff --git a/source/_posts/2024A-rcore-camp-stag2-moyigeek/file.jpg b/source/_posts/2024A-rcore-camp-stag2-moyigeek/file.jpg new file mode 100644 index 00000000000..a1692bfe717 Binary files /dev/null and b/source/_posts/2024A-rcore-camp-stag2-moyigeek/file.jpg differ diff --git a/source/_posts/2024a-rcore-srcres258-stage1.md b/source/_posts/2024a-rcore-srcres258-stage1.md new file mode 100644 index 00000000000..b5085c02138 --- /dev/null +++ b/source/_posts/2024a-rcore-srcres258-stage1.md @@ -0,0 +1,22 @@ +--- +title: 2024 秋冬季开源操作系统训练营 第1阶段(基础阶段 - Rust编程) +date: 2024-11-12 21:54:11 +category: srcres258 +tags: srcres258 +--- + +# 阶段目标与晋级条件 + +- Rust编程语言为学习操作系统设计与实现打下坚实基础 + +- 通过完成100道Rustling编程题,强化训练Rust编程技能 + +- 该阶段排行榜达到满分可晋级,进入专业阶段学习 + +# 阶段个人总结 + +Rust 语言是参加本训练营必须掌握的一门语言,因为本训练营的所有项目均基于本语言编写。由于本人已学习过 Rust 编程语言,故本阶段无太大压力,跟着晋级要求完成 rustlings习题后即达成目标。 + +而重点是后续专业阶段 rCore 操作系统的学习与代码理解,因操作系统相关知识早已忘记(虽然曾跟着网络教程阅读过 Linux 内核源码,但年代久远早已忘记细节),故需要一定时间重拾相关知识,并跟着实验在旧知识基础上提升自己的理解水平。 + +附:本人学习 Rust 语言的主要参考[视频教程](https://www.bilibili.com/video/BV1hp4y1k7SV/)。 \ No newline at end of file diff --git a/source/_posts/2024a-rcore-srcres258-stage2.md b/source/_posts/2024a-rcore-srcres258-stage2.md new file mode 100644 index 00000000000..cb647c47d14 --- /dev/null +++ b/source/_posts/2024a-rcore-srcres258-stage2.md @@ -0,0 +1,20 @@ +--- +title: 2024 秋冬季开源操作系统训练营 第2阶段(专业阶段 - OS设计实现) +date: 2024-11-12 22:04:16 +category: srcres258 +tags: srcres258 +--- + +# 阶段目标与晋级条件 + +- 从零开始构建操作系统的各个模块,不断完善操作系统核心功能 + +- 完成5道rCore操作系统大实验编程题,深入领会OS重要概念,掌握必备技能 + +- 排行榜积分达到500分,方可进入项目阶段学习,参与团队合作完成关键任务 + +# 阶段个人总结 + +从本阶段开始本人感到明显的学习吃力,各种新概念在教学过程中如泉涌般浮现在脑海,难以在短时间内完全消化完毕。 故专门抽出一整个周末以及课余时间反复研读与理解教学文档,深入洞悉各种新概念的内涵,在彻底理解相关知识点后再结合视频直播中老师的讲解理解每一行代码的实现逻辑(5W1H原则:‌Why(何因)‌、‌What(何事)‌、‌Where(何地)‌、‌When(何时)‌、‌Who(何物)‌、‌How(何法))。在确保完全弄懂文档知识点与代码的实现逻辑之后,方才着手完成每个实验的习题。 + +通过本阶段的学习,我也认识到学习操作系统相较于其他编程实践项目需要投入更多的精力,对于陌生的技术栈需要花更多的心思去理解其技术细节与应用方式,不能向以前一样盲目自信而心不在焉地学习。同时这几次实验也建立起我实战大型编程项目的经验,为以后参与更多更繁杂的编程项目打下基础。 \ No newline at end of file diff --git "a/source/_posts/2024\345\271\264\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\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-guoraymon.md" "b/source/_posts/2024\345\271\264\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\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-guoraymon.md" new file mode 100644 index 00000000000..d50aceb86e8 --- /dev/null +++ "b/source/_posts/2024\345\271\264\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\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-guoraymon.md" @@ -0,0 +1,20 @@ +--- +title: 2024年秋冬季开源操作系统训练营一、二阶段总结报告-guoraymon +date: 2024-11-11 16:05:18 +categories: + - 2024秋冬季开源操作系统训练营 +tags: + - author:guoraymon + - repo:https://github.com/LearningOS/2024a-rcore-guoraymon +--- + +## 第一阶段 +好几年前就被 Rust 的性能和并发功能吸引,无奈日常没有运用,只能学而时习之。不过 Rusting 还是没有什么难度的,多看看文档,轻松拿下不是问题。 + +## 第二阶段 +第二阶段就需要了解操作系统原理了,各类教科书都停留在原理阶段,学完了也不知道自己学会了没有。 +lab1 比较简单,原理是需要了解特权级和系统调用,实践上懂得在哪里记录数据和返回即可。 +lab2 难度提升不小,原理上需要了解页表机制,多级页表极其抽象,算是一个难点。实践上要注意内核态拿到的是用户态的虚拟地址,要注意转换而不是直接访问。 +lab3 难度不大,原理上就是进程那些事,不算难。实践上因为基础代码都已经提供了,依样画葫芦很容易就能通过。 +lab4 感觉也是不算难的,原理上就是各种数据结构的管理。实践上因为现在的文件系统比较简易,很多可以不过多考虑直接取巧实现。最大的问题是我的机子性能太差,跑全部测试必定超时,我误以为是自己代码问题。 +lab5 感觉是个巨坑,原理上实验说明写的模模糊糊,完全看不懂。实践上也是感觉测例不完善,虽然顺利通过了,但是没把握实现是正确的。 \ No newline at end of file diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack.md" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack.md" new file mode 100644 index 00000000000..33ad5d9ffec --- /dev/null +++ "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack.md" @@ -0,0 +1,224 @@ +--- +title: 2024秋冬季开源操作系统训练营-一二阶段总结-Zack +date: 2024-11-11 11:51:17 +categories: + - report +tags: + - author: Zack + - repo: https://github.com/LearningOS/2024a-rcore-lzcers +--- + +# 感想 + +在接触这门课程之前,我正处于第 N 次入门 Rust 的途中,在之前其实就一直有过多次学习 Rust 的经历,我博客上关于 Rust 的学习笔记最早甚至是 18 年的,回顾我最早学习 Rust 的理由是我希望掌握不同的编程语言范式拓展自己的思维模型。 + +> 「学习过程更多的是知识迁移,如果是学习新的编程范式则会稍有难度,这不仅仅是学习新的概念,还需要在思维模式上做出改变,对于编程语言的学习,应当多去学习不同范式的语言,尽管现代编程语言都支持多范式,但各语言擅长的范式还是不一样的。」 + +但前几次入门 Rust 多因为工作没能用上而浅尝辄止,最近一次我是深刻意识到不实践是无法学会的,所以我一边用 Anki 制作学习笔记卡片,一边尝试用 Rust 写一些小工具,又在 Rust 微信群里看到了 Rust 操作系统的课程,这一下死去的记忆突然开始攻击我了,这不正是我想要的实践吗? +回想起大学自己自学过王爽的《汇编语言》还有于渊《一个操作系统的实现》,但止于汇编编写内核引导后输出 Hello World 的阶段,我被开篇的复杂 C 语言应用劝退了,上面两本书的作者和名字我时隔十年居然都能清晰回忆起来,这或许与我当时的热情有关吧,毕竟操作系统是三大浪漫之一。 +于是我立了个 FLAG 想要弥补过去没学好的内容,另外也是为了真正知道自己在做什么,当我拿起自己习以为常且熟练的编程语言和计算机工具进行各种创作的时候,真正发生了什么,学完二阶段或许我不能说掌握了多少底层的细节,但是我有了一种清晰地感知,当我写下的代码在计算机中流动的时候发生了什么。 + +# 控制流 + +由代码编写的指令在编译成汇编代码后会形成一个执行序列,这个序列就是一个控制流,在正常没有中断和异常情况下,整个控制流都由代码生成的汇编控制,这个控制流是**普通控制流。** +但是一旦进行系统调用,程序就会脱离原本的控制流,进入到操作系统内核代码的控制流中了,比如接受 IO 中断响应,处理用户输入等。这种突变的控制流称之**异常控制流**。(处理中断、异常、陷入) +这个过程中伴随着执行环境的变化,以及上下文的切换,在应用程序基于操作系统抽象(进程、线程等)的执行环境中,突然切换到操作系统内核基于硬件的执行环境中,同时上下文发生了变化(执行环境的相关参数)。 +操作系统和应用程序需要协同硬件一起来保存和恢复这些上下文,使得程序能够正常运行。 + +异常控制流的保存和恢复由操作系统和 CPU 负责。(手动编写在栈上保存与恢复寄存器的指令) + +对于函数转移这内控制流转移,由编译器负责。(编译器会自动生成栈上保存与恢复上下文的指令) + +# 异常控制流 + +外设中断(Device Interrput):由外部设备引起的外部 I/O 设备事件。 +异常(Exception):程序发生除零错误,内存访问越界等。 +陷入(Trap):系统调用进入操作系统工作流。 + +三种东西都是一回事,都是应用程序的工作流被中断了,跑去执行别的地方的代码了,不过对三种中断的方式做了命名区分。 + +# 目标平台与目标三元组 + +通常一个 C 程序编译器的工作流程如下: + +1. 源代码 -> 预处理器进行宏展开 +2. 宏展开的源代码 -> 编译器编译生成汇编代码 +3. 汇编程序 -> 汇编器编译为目标机器代码 +4. 目标代码 -> 链接器链接为可执行文件 + +Rust 通过一个三元组来描述软件运行的目标平台,即 CPU、操作系统、运行时库等。 + +`$ rustc --version --verbose + +``` +rustc 1.82.0 (f6e511eec 2024-10-15) +binary: rustc +commit-hash: f6e511eec7342f59a25f7c0534f1dbea00d01b14 +commit-date: 2024-10-15 +host: aarch64-apple-darwin +release: 1.82.0 +LLVM version: 19.1.1 +``` + +从 host 可以看出我们的 CPU 是 aarch64, apple 系统, darwin 运行时。 + +# Rust std 库和 core 库区别? + +std 库依赖操作系统,提供线程等能力,core 库不依赖操作系统。 + +elf 格式分析 +`rust-readobj -h target/riscv64gc-unknown-none-elf/debug/os` + +# 实现第一个裸机程序我们做了什么? + +Rust 编程:剥离标准库,使用 core 库提供的 trait 实现 panic_handle + +## 1. 去掉 Rust 的标准库依赖,使得程序能够编译成功。 + +1. 在 Rust 项目中使用 `#[no_std]` 标识不使用标准库,这样一来我们 `println!()` 也将无法使用需要自己实现。 +2. panic 处理是必须的,但是 panic!宏也标准库实现,因此需要导入 core 库手动实现 `panic_handle` 方法 +3. 但程序需要一个的启动需要一个`_start` 语义项,语言的标准库作为程序的执行环境,在启动前也会做一些初始化工作才会跳转到由用户编写的程序入口处,但此时我们已经没有标准库依赖了,因此`#[no_main]`声明没有通常意义的入口函数。 + +完成上面三项我们就可以编译通过了,但此时编译出来的是一个空壳应用,我们希望执行点什么。 + +## 2. 编译出能对接 Qemu 与 RustSBI 的应用。 + +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/20241101143428.png) + +源代码编译出的来程序字节码可以分为两部分即数据与代码,实际上我们还可以对这两部分进一步划分为更小的单位段(Section),整个这部分就构成了程序内存布局。 + +.bss: Block Started By Symbol 存储未初始化的全局变量和静态变量。 +.data: 已经初始化的全局变量与静态变量。 +.rodata: 存储只读的常量与字符串等。 + +1. Qemu 在加电启动后会从 0x80200000 开始执行,因此我们需要调整编译出的程序的内存布局,这将通过为编译器指定 `linker.ld` 配置文件来实现,但提交给 Qemu 的文件还要剥离元数据才能被 Qemu 正确寻址,这通过 `rust-objcopy --strip-all` 来实现。 + +1. 使用`#[no_mangle]`自定义的入口函数 +1. 编写 linker.ld 定义内存布局 +1. 编写汇编代码配置栈空间布局,设置栈顶指针然后 call 入口函数 + +```asm + # os/src/entry.asm + .section .text.entry + .globl _start + _start: + la sp, boot_stack_top + call rust_main + + .section .bss.stack + .globl boot_stack +boot_stack: + .space 4096 * 16 + .globl boot_stack_top +boot_stack_top: +``` + +## 3. 实现关键系统调用,实现 Println! 宏等。 + +通过系统调用实现退出机制,通过系统调用实现 print 方法以及宏。 + +# 如何实现批处理操作系统? + +批处理操作系统主要解决几个问题: + +1. 加载所有应用程序 +2. 在执行应用程序前初始化,比如用户态栈的初始化,特权级别切换 +3. 处理应用程序的系统调用 +4. 处理应用程序的错误,在出错时执行下一个程序 + +因此首先要实现用户态的程序,剥离元数据转为 bin 格式,以静态绑定的方式载入内存,在使用时以动态加载的方式来加载。 +具体实现方法是通过代码生成汇编码,将应用程序写入数据段,并标识每个程序的起始结束地址。 + +```asm +# 例子 + .section .data + .global app_4_start + .global app_4_end +app_4_start: + .incbin "../user/target/riscv64gc-unknown-none-elf/release/04priv_csr.bin" +app_4_end: +``` + +在内核中,实现应用程序的加载器、实现 Trap 的处理和特权级别切换,以便于在用户态程序进行系统调用时从用户态切换到内核态,并且在这种切换过程中实现上下文的保存和恢复。 + +## CSR 是什么? + +在计算机体系结构中,CSR 通常指的是 "Control and Status Register"(控制和状态寄存器)。这些寄存器用于存储处理器的控制信息和状态信息,允许操作系统和应用程序控制处理器的行为和获取处理器的状态。 + +# 多道任务与分时多任务 + +在这里我们主要为了隔离 CPU 资源的使用,让每个应用在特定时间内都能获得 CPU 的全部使用权,同时也可以主动让出 CPU 的使用。 +为了让内核同时调度多个应用程序的执行,我们需要实现应用程序的加载机制和任务管理系统,实现任务的切换、暂停管理等能力。 + +## 协作式多任务执行 + +通过实现 **yield 系统调用** 我们可以让任务主动让出 CPU 使用权,从而让内核进行任务切换执行别的任务,这种由任务自己让出 CPU 使用权的系统叫做**协作式**操作系统,但是任务下次获得 CPU 使用权的时间与内核调度策略以及其它任务相关,所以缺点也很明显,对于需要及时得到响应的任务而言这样协作式的方式会严重影响使用体验,协作式操作系统适用于所有应用都是可信的情况下。 + +## 分时多任务执行 + +通过时钟中断机制,我们可以实现时间片轮转调度机制,让 CPU 在每个时间片段内周而复始的轮转执行任务,这样可以确保每个任务都能公平的得到 CPU 的使用时间,这种机制叫做**抢占式**调度策略。 + +# 地址空间 - 计算机空间的抽象 + +现在,我们学会了如何将如何将应用运行在裸机上所必要的知识与概念,即特权级别的切换与任务栈的切换,切换过程中伴随的寄存器的存储与恢复,接下来是操作系统最重要的事物,即构造各种抽象,提供环境约束与管理。 +第一要构造的抽象是「**地址空间**」,它给应用程序创造一个拥有全部内存的幻觉,隔离应用以及操作系统之间的内存访问,保证安全。 + +## SV39 多级页表机制 + +RISC-V 中通过将 Satp 寄存器的高 8 位 Mode 设置为 8 则开启 SV39 页表机制,此时 U/S 模式下的地址访问都会被视作 39 位虚拟地址需要经过 MMU 转换为 56 位的物理地址,SV39 即特权模式下的 39 位虚拟地址,即在 SV39 模式下,64 位宽只有低 39 位是有意义的。 + +**地址格式:** +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack//Pasted%20image%2020241106141334.png) + +因为我们的页表是 4K 大小,因此需要 12 位来寻址,因此低 12 位为页内偏移,高 27 位则为页码,MMU 的转换就是扎到虚拟页码到物理页的映射,Offset 不变,拼接成 56 位物理地址。 + +**页表格式:** +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241106144107.png) + +**页表存储:** +如果按下面线性表的方式存储页表,即知道一个应用页表的 base*addr,对应的虚拟页页表 = base_addr + 8 * 虚拟页码,即可得到页表项从而找到物理地址。 +考虑到虚拟地址有 27 位用于页表索引,因此如果完全存储 2^27 页页表需要 2^27 \_ 8(64 位) = 1GB 的空间,这显然不现实。 + +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241106144124.png) + +因此我们需要按需分配的机制,即保存真正有效的虚拟页表。 + +我们可以通过类似前缀树的方式采用三级页表的方式来索引页表项目 PTE,将 39 位虚拟地址的高 27 位分为 3 段,每段 9 位,剩下 12 位为偏移,如下图所示,通过三级索引我们拿到最终的物理页表页码,加上 Offset 就可以得到最终的物理地址。 +每一级页表的大小刚好 2^9 = 512 个 PTE,每个 PTE 大小 8Byte 刚好 4K 一个物理页帧,这样一来,12K 就足以存储页表了。 +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241107161803.png) + +## 在地址空间下运行的应用 + +在地址空间下应用的程序与之前最大的区别在于,此时所有地址指令都需要经过多重的转换,期间有 TLB 加速了这个过程。除了之前提到的特权级别切换,我们还要切换相应的地址空间。 +为了这种切换能够顺畅,我们需要构造跳板以及陷入上下文等数据结构,在应用的高位地址存储用户态陷入上下文以及映射陷入内核的跳板代码,这部分代码尽管在用户地址空间,但是 S 态才有的权限。 + +# 进程 - 计算器力量的抽象 + +计算是一种强大力量,但是在操作系统上可能同时运行着多个不同的应用程序,每个应用都可能需要持有计算的权柄,前面提到我们可以让程序自己出让计算权利的协作式多任务模式,也有由操作系统根据时间片切换的抢占式模式。 +如何更好的管理这些任务的计算,我们需要更高阶的抽象,即进程,可以理解为比任务更高阶的概念,它不光包括了正在运行的任务,还包括了他拥有的资源,如地址空间等,下面这张图很好的描述了进程概念和任务的执行过程。 + +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241111123056.png) + +# 文件系统 - IO 的抽象 + +关于 Linux 系统有一句非常著名的话,那就是一切接「**文件**」,这个概念如此的强大,以至于可以容纳一切外部设备,仔细想想,因为它只包含了最小的内涵,即「**读、写**」两个操作,只要能读写?那就是文件,包括我们的硬盘。 + +准确的说,文件系统是建立在块设备上的一种抽象,下面这张图描述了块设备的内部布局,类似于页表,不过用于管理索引的结构叫做位图。 +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241111123713.png) + +操作系统上的应用不应关注块设备的内部结构,而是需要关心它提供什么能力,如果每次数据的读写都需要从索引一路找到数据块岂不是很麻烦。 +因此我们要为其实现目录结构的管理,这是一个内存中的数据对象,它完成了对块设备布局中存储的真正数据内容的映射。 +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241111124043.png) + +# 并发 - 万物迸发 + +## 死锁检测 + +算法过程就是判断某一未结束线程 Finish[i] == false +它的需求是否小于操作系统所能提供的 Need[i, j] <= Work[j] + +如果小于那就执行到完成, 然后更新操作系统能分配的资源 +Work[j] = Work[j] + Allocation[i, j] +Finish[i] = true + +如果 Finish[0..n-1] 都为 true 即所有线程都有足够资源,否则就是不够,有死锁。 diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/20241101143428.png" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/20241101143428.png" new file mode 100644 index 00000000000..55cf0638093 Binary files /dev/null and "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/20241101143428.png" differ diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106141334.png" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106141334.png" new file mode 100644 index 00000000000..b5980901d45 Binary files /dev/null and "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106141334.png" differ diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144107.png" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144107.png" new file mode 100644 index 00000000000..5431989103f Binary files /dev/null and "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144107.png" differ diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144124.png" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144124.png" new file mode 100644 index 00000000000..0f69653abaa Binary files /dev/null and "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144124.png" differ diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241107161803.png" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241107161803.png" new file mode 100644 index 00000000000..d00969376b8 Binary files /dev/null and "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241107161803.png" differ diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123056.png" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123056.png" new file mode 100644 index 00000000000..b935b9d13e0 Binary files /dev/null and "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123056.png" differ diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123713.png" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123713.png" new file mode 100644 index 00000000000..6f59c37d468 Binary files /dev/null and "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123713.png" differ diff --git "a/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111124043.png" "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111124043.png" new file mode 100644 index 00000000000..acfc0c15418 Binary files /dev/null and "b/source/_posts/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-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111124043.png" differ diff --git "a/source/_posts/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\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\347\216\213\347\277\212\345\230\211.md" "b/source/_posts/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\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\347\216\213\347\277\212\345\230\211.md" new file mode 100644 index 00000000000..14b59c0e330 --- /dev/null +++ "b/source/_posts/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\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\347\216\213\347\277\212\345\230\211.md" @@ -0,0 +1,106 @@ +--- +title: abcd1234-rust-os +date: 2024-10-15 18:32:19 +categories: + - rust-stage-1 +tags: + - author:17999824wyj + - repo:https://github.com/LearningOS/2024a-rcore-17999824wyj + - noob + - nickname:abcd1234 + - email:abcd1234dbren@yeah.net +--- + +# Rust OS 一阶段学习总结 + +@FinishTime: 2024-09-19 00:35:51 + +## 个人背景概述 + +我是一名软件工程专业的大三本科生,曾参与过 2023 年秋冬季训练营和 2024 年春夏季训练营。并且在 2024 季春夏训练营中,取得了通过的成绩。 +今年的秋冬季训练营,我想冲一冲优秀,多做一些项目,多为国内操作系统和开源做出贡献。 + +## 今年一阶段学习时间表 + +- 2024-09-18 大约 3.5 小时,重新完成 rustlings 的官方基础习题(前 94 题)。之后,又用了大约 2 小时,完成了后续的习题(95-110)。 + +## 一阶段学习内容概述 + +我按照[“rust 语言圣经”](https://course.rs/about-book.html)上的讲解顺序,复习之前的知识点。 +由于我在那段时间之前,在参加 InfiniTensor 的 AI 训练营,所以 rust 的基础语法等根本没有落下,这使得我在完成 rustlings 的习题时,几乎没有遇到什么问题。 + +只有到后续的习题中,我遇到了一些问题,主要是和 `智能指针` 有关的内容。这些部分我一直不是很理解的。但是因为我的经验,我还是能写出代码的。 + +## 总结 + +在第一阶段的学习中,我巩固了我所掌握的 rust 基础,我更是深深的意识到:`Talk is cheap, show me the code!`。实践是最好的老师! + +# Rust rCore 二阶段学习总结 + +@FinishTime: 2024-10-15 18:44:32 + +## 二阶段学习时间表 + +- 2024-10-08 开始实验,轻车熟路,一天晚上完事了大部分的 ch3。 +- 2024-10-09 凌晨 ch3 debug 完毕,下午进行提交完毕。 +- 2024-10-09 晚上 ch4 提交完毕。 +- 2024-10-11 ch5 提交完毕。 +- 2024-10-13 ch6 提交完毕。 +- 2024-10-15 ch8 提交完毕。撰写报告完毕。 + +## 问题复盘 + +通过上面的学习时间表可以看出,我虽然是第二次做大实验,但仍然每一部分都需要 1 到 2 天时间。虽然不是将大段时间全部投入,但可能也是比较慢了。 +以下是我实验过程中的问题复盘。 + +### ch3: 本地测例初始化问题 + +在 ch3 里,我遇到了两个问题,第一个是本地测例的初始化问题。 + +由于我是第二次参与训练营,所以在实验时,我就直接去看指导书的习题部分,在尝试开始实现时,在 rust-anaylzer 的使用上时发生了错误,插件因为找不到某些依赖库,拒绝提供服务。 + +我去查看了插件的报错信息,发现找不到 `user` 目录。而我在源代码仓库,也确实没看到源代码里有 `user` 目录。后续,我在自行尝试解决无果后,去微信群问了助教。得到的回答是 `看文档`。然后我去重新看了文档,找到了一个下载 `user` 的方法。这个问题是由于我的粗心导致的。 + +### ch3: 时间初始化问题 + +ch3 的第二个问题是,os 时间初始化问题。 + +在我实现的 os 里,在时间初始化,采用的是 `直接在任务加载时便初始化为当前系统时间`。这显然是错误的。但它的报错并不友好。 + +此问题的表现为:在直接指定 `BASE=1` 执行测试用例时,能够通过测试用例。指定 `BASE=0` 时,也能够通过测试。但指定 `BASE=2` 时,竟然不能通过测试!?同时,这种情况下,qemu 会出现“卡死”,让人一直等下去。 + +我通过细细阅读报错,发现 qemu 卡死是因为有一个用户测例 panic 导致的。经过分析的测例执行逻辑为: + +```plaintext +我分析,ch3里,qemu在跑测试用例的时候,qemu的退出机制是,当全部测试用例成功时,才会退出。 + +如果有测例没过,他会接着执行别的测试用例,但最后全部测试用例执行完后,就不会退出了,还没有信息提示。 +``` + +能够定位位置,之后的工作就要简单多了。我发现了问题所在,然后将时间改为 `Option` 类型,以 `None` 来初始化,在为某任务进行 sys_call 计数时进行判断,如果为 `None`,则进行初始化。这样,问题就解决了。 + +后续,我看到交流群里有个同学,和我遇到了一样的问题,于是,我也帮助了他。 + +### ch6: 又一次卡了一小点 + +在 ch6 时,平心而论,要求实现的逻辑并不是很复杂,只是需要层层的传递,最后由“easy-fs”进行实际干活即可。但我再次遇到了一个极为恶心的问题。 + +在春夏季训练营,我曾经遇到了“File is shorter than the first ELf header part”的问题,翻译过来就是,文件比文件描述符还短。我发现,只有 ch5_spawn0,也就是 helloworld 的测例出现了这个问题。 + +而这一次,我又遇到了这个问题。然后,我通过微信群的聊天记录,找到了当时一位热心同学给我的解答,即,此情况可能是由于 rust 的智能优化导致的,只需要把一个位置,由 `[u8; 512]` 改为一个 Vec 即可。 + +还好我留有聊天记录,再次感谢当时的那位同学! + +### ch8: 系统时间系统调用未实现 + +来到了 ch8,我没有看到指导书文档里,写着的 “除 sys_get_time 以外”,所以,我就没有合并我之前的代码。但问题是,在我实现了 ch8 的内容后,进行测例,有两个始终过不去,就是那两个死锁相关的。而且,它没有报错提醒。这使我很懵,是不是我的银行家算法写的有问题? + +在一次又一次地尝试为银行家算法进行 debug 后,我发现,好像我的算法没问题啊。于是,我又一次详细阅读测试代码,发现:这里面有个`sleep(500)`,这是调了哪个 sys_call?我查了,发现是获得系统时间的那个。但我没有合并之前的代码,导致其会进入死循环。由此,我才改好了问题。 + +## 总结 + +第二次来 rCore,感觉熟能生巧了一些,之前很多不太理解的部分,已经慢慢构不成威胁了。果然,实践能够带动理论!但我也因为自己的粗心,导致了一些不必要的麻烦。这是需要我进行反省的。 + +## 展望 + +这一次的三阶段,将会涉及到“组件化”。希望我能尽快完成相关内容,及早进入后续项目内容的学习!希望能够冲一冲优秀! diff --git "a/source/_posts/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\346\200\273\347\273\223-\346\235\216\346\254\243\350\224\223.md" "b/source/_posts/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\346\200\273\347\273\223-\346\235\216\346\254\243\350\224\223.md" new file mode 100644 index 00000000000..eac8163f8b6 --- /dev/null +++ "b/source/_posts/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\346\200\273\347\273\223-\346\235\216\346\254\243\350\224\223.md" @@ -0,0 +1,64 @@ +--- +title: 2024年秋冬季开源操作系统训练营-Sylvia +date: 2024-11-10 00:39:53 +categories: + - report +tags: + - author:SylviaSylviaSylvia + - repo:https://github.com/LearningOS/2024a-rcore-SylviaSylviaSylvia + +--- + +### 开篇想先说点废话 +说实话真的挺感慨的,竟然真的顺利的做到了今天,竟然真的得到了500分。 + +我是一个小城市长大的孩子,虽然有幸出生于学历较高的家庭,但毕竟环境、观念等受限,我并没有像大城市的孩子一样有机会从小开始学习计算机。准确的说,我连电脑也几乎没有碰过,因为我家的电脑是对我设了密码的,而我的妈妈也比较反对我使用电子产品。 + +再加上年幼的我鼠目寸光,眼里只有考试成绩,小初高阶段我选择了在每一门不考试的科目,包括计算机课上,写物理数学作业。 +2022年8月,我考入大学。因为性格等原因,我报的志愿全部都是工科专业。虽然我很恐惧计算机,认为自己的基础根本不配学,但作为工科专业里的热门专业,我仍然把它排在了志愿前列。 + +很不巧,我刚好是计算机专业的分数线,但刚好未录取。我去了其他的工科专业。 + +于是,我的大一,也依然保持着高中的习惯,电脑常年放在抽屉里,认为拿出来占地方。如果在学习那基本就是做数学物理题。 + +变化发生于我转专业进入计算机大类。其实也许也不算吧,我是网安专业的,在很多学校可能也不算计算机。选择的原因呢,一个是我学校这个专业保研率真的很高,另一个是它看起来没有计科那么需要优秀的代码编写能力。我的必修课程有408四门,有编译原理等计算机常见课程,却连java和python都没有正经学过。不过那都不重要。 + +转入计算机大类,我就顺理成章的进入了计算机大类的学生群。在和各种计算机类专业的优秀同学的相处中,我竟慢慢的发现我爱上了计算机。 + +我开始尝试自学,并想要跨专业考研。选好目标院校后,我进入考研群了解相关消息,并在群里看到了训练营的信息。 + +这就是我与训练营相遇的开端。 + +当然,那不是2024秋冬季训练营。是春夏季训练营。 + +### 2024春夏季训练营,败给了自卑与拖延 +前面也提到了,我的计算机基础非常差,大二之前可以说是彻彻底底的零基础。大二刚转专业,又总抱着一种我大一学的c++比他们简单,又没学过好几门专业课的先修课,更加放纵着自己选择逃避。参加训练营时,又以自己年纪尚小、也没怎么学过(刚好是大二第二学期学,当时正在学)操作系统为由纵容自己拖延,直到时间不够为止。第一阶段内容不多,勉强完成。第二阶段,一题都没来得及做。 +我的第一次挑战,就这样结束了。 + +### 2024秋冬季,我又来了 +明年要开始复习考研初试,不会再有时间。我知道,这是我的最后一次机会了。说实话我自身实力并没强多少,但是没有退路的我选择了尽最大可能试一试。这一次,我希望我也可以顺利做到四阶段结束。 + +### 关于训练营本身,我的学习与解题过程 +进入正题。 +* 第零章、ch1、ch2: + 主要是下载了一些东西,对二阶段有了一点初步的了解,学习了git的分支是什么【终于意识到二阶段仓库不是只有一个.md文件】 + +* ch3: + 第一次在内核里写代码,说实话抵触心理强得可怕,依然是还没开始就开始暗示自己:我肯定写不出来的,我肯定看不懂的。 + 感谢我没有退路,硬着头皮也得写。历时三天,终于在一个周一的早晨顺利通过。我的代码应该是有很多冗余处的,但我依然很兴奋。因为这真的是我第一次纯靠自己动脑子想、去尝试、去不断地修改bug,写出来的代码。 + 以及第一次学会了git如何提交到一个分支上。 + +* ch4:虚存! + 可能也不一定是五个lab里最难的一个,但却是我记忆最深的一个。虚存是我上学期操作系统学的最差的地方,多级页表的题我几乎做一道错一道,我很恐惧这里。看书,尝试,发现自己还是不理解,又看书,又尝试...草稿纸足足用了小半本,一个垃圾桶都差点没塞下hh。开始时最不能理解的是sys_get_time和sys_task_info的改写,我一点也想不明白他们哪里使用了地址。研究了好几天才意识到:ti和ts就是指向虚拟地址的指针,所以要转换成物理地址才能保存信息。 + 以及,终于意识到最初提供的代码里传入的参数前加下划线是未被使用的意思,所以我不需要使用_ti等作为变量,我使用时可以直接删去下划线hhh + +* ch5、ch6、ch8: + 这几个因为本身纯底层理论的东西学的还行,逐渐也开始敢于尝试不妄自菲薄了,其实也没什么特别大的感悟。就是平均三天一章的边研读文档边尝试着写。 + 闭包函数很有意思!但是我还是有点没太摸透,还需要今后再深入学习。 + ch6写sys_fstat时忘了虚存映射,一晚上都在报错time_out,睡前才突然意识到。以后写代码一定要更加注意细节。 + +### 感谢 +真的非常非常非常感谢开源操作系统训练营能给我这么一次机会去参与学习一门新的语言以及内核相关知识。在这个过程中我不仅加深了对操作系统的理解,也逐渐培养起了自己解决问题,不轻言放弃的习惯。我第一次去将一个大的长期任务分隔成小任务,规划什么时候做完哪些,而不是全部拖延到截止日期再痛苦。也是第一次清晰的意识到:哇原来我也可以自己写出较长的代码!也很感谢群友们回答我的一个个过于基础导致对擅长计算机的人大概有点搞笑的问题。谢谢每一位训练营的创办者、负责人、讲师、助教、同学们。 + +### 补充 +补充后的内容为2024.11.11二次提交pr时增加。为向给负责拉取pr的老师带来麻烦致歉,以及感慨这段意想不到的小插曲,增加了这一小段。感谢训练营,春夏季训练营是我第一次使用github,秋冬季训练营是我第一次提交pr。我学到了很多关于git的知识,也因为开始频繁打开github,在github上见到了很多好的项目与知识。我一直很好奇git是怎么工作的,企业中团队不同成员又是如何通过git来合作的,都在训练营使用git的过程中得到了解答。我第一次知道github的不同分支,也第一次知道了提pr是什么样的一个流程。也是第一次知道,git add . 真的不能随意使用,会将自动修改而不应该被修改的文件也修改。git很有意思也很有用,希望我也能有一天真的和一群人一起合作开发一个项目。再次感谢训练营,感谢训练营的老师们! \ No newline at end of file diff --git "a/source/_posts/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\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rjy.md" "b/source/_posts/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\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rjy.md" new file mode 100644 index 00000000000..9778890edab --- /dev/null +++ "b/source/_posts/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\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rjy.md" @@ -0,0 +1,34 @@ +--- +title: 2024秋冬季开源操作系统训练营第一、二阶段总结报告-rjy +date: 2024-11-10 22:41:54 +categories: + - blog +tags: + - author:RDWaaaaaa + - repo:RDWaaaaaa/rust-based-os-camp-blog +--- +## 第一阶段 + +之前自己写过rustlings的一部分,这次是完整地完成了。rust和其他语言的不同之处在于,所有权、借用和引用检查等,在内存管理、安全性、并发性方面都有其优势和特点。 + +## 第二阶段 + +这段时间的操作系统学习让我了解了计算机系统中各个模块的结构和交互过程。从搭建实验环境到实现进程管理、文件系统与并发控制,我逐步掌握了操作系统的核心概念与实现方法。 + +在实验环境配置上,通过熟悉 QEMU 等模拟工具的配置,我能够创建隔离的测试环境,保证了开发过程的安全性和可控性。紧接着,在应用程序与基本执行环境部分,我学习了如何在内存中定位程序的不同部分,了解了程序的加载和执行过程,打下了对操作系统基本管理能力的初步认知。 + +随着课程深入,我逐渐接触到批处理系统和多道程序设计,进一步理解了操作系统资源分配与调度策略。这帮助我认识到系统资源的有限性,以及多任务分配中提高资源利用率的必要性。在此基础上,我学习了进程及地址空间的相关知识,理解了分时多任务的原理,并学会了如何通过调度算法提升系统响应速度和处理效率。 + +在进程管理与进程间通信部分,我逐步掌握了如何创建和管理进程,以及进程如何在系统中相互通信。这些内容让我理解了操作系统在多任务处理中的关键作用,也使我了解到不同进程通信方式的特点和应用场景。 + +最后,通过对文件系统与 I/O 重定向的学习,我了解了数据存储、访问控制与设备交互的基本机制。此外,并发编程的知识让我对线程和同步控制有了更清晰的认识,并学会了如何在多任务环境下避免资源竞争和死锁问题。 + +总体而言,这次学习不仅使我对操作系统的工作原理有了系统化的理解,还培养了我的编程能力和调试技能。操作系统涉及的原理性问题较多,需要不断在实践中加深理解,也让我认识到学习操作系统的深度和广度,为未来进一步的深入学习打下了坚实的基础。 + +### 收获 + +因为之前没有学习过操作系统,每个任务对我来讲都很困难,这次算是入门。 + +### TODO +有很多代码在写lab的时候实际上还没仔细看。 +有些功能可能写的不够漂亮,test偏弱,可能还有一些错误没有被发现。 diff --git "a/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" "b/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" new file mode 100644 index 00000000000..49a4465494a --- /dev/null +++ "b/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" @@ -0,0 +1,174 @@ +--- + +title: rust体会 +date: 2024-11-11 +categories: + - rust language +tags: + - author:ekkure + - repo:https://github.com/ekkure/blog +--- +### Rust编程技巧与示例:宏、算法与类型转换 + +在Rust编程中,有许多细节和技巧可以帮助开发者更好地组织代码、优化算法性能,以及确保类型安全。本篇博客汇总了一些Rust编程的核心要点和实用代码示例,涵盖了宏的使用、排序算法、树和图的操作等内容。 + +--- + +### 1. 宏与#[macro_export]、#[macro_use] + +Rust中的宏非常强大,用于生成重复代码和提升代码的灵活性。使用`#[macro_export]`可以导出宏,使其在其他模块或包中可用;而`#[macro_use]`则在现代Rust中被推荐通过`use`语句显式引入。 + +示例宏定义: +```rust +#[rustfmt::skip] +macro_rules! my_macro { + () => { println!("Check out my macro!"); }; + ($val:expr) => { println!("Look at this other macro: {}", $val); }; +} +``` + +这里的`#[rustfmt::skip]`用于避免自动格式化,保持代码样式的灵活性和可读性。 + +--- + +### 2. Rust中的类型与特性 + +在实现数据结构或算法时,我们通常需要对泛型类型T施加一些特性约束,例如: +- `Ord`:使得元素可以比较大小,适用于排序、合并等操作。 +- `Clone`:便于复制元素值,即使是复杂类型,也可以无所有权转移地复制。 +- `Display`:实现字符串友好的格式化输出,便于打印和日志记录。 + +这些特性可以通过`where`语句在泛型实现中指定: +```rust +impl LinkedList +where T: Ord + Clone + Display +``` + +--- + +### 3. 内存操作与指针 + +Rust通过`unsafe`块支持手动管理内存和指针操作,用于高性能或底层操作。 +例如,获取节点的指针并解引用: +```rust +let node_ptr = Some(unsafe { NonNull::new_unchecked(Box::into_raw(node)) }); +res.add((*node_ptr.as_ptr()).val.clone()); +cur_a = (*node_ptr.as_ptr()).next; // 注意这里直接获取的是ta的next指针 +``` + +指针的安全解包和操作要格外小心,可以使用`Option`配合`unsafe`避免空指针风险。 + +--- + +### 4. 算法设计示例 + +#### 4.1 链表与树的操作 + +##### 插入与查找 +在链表或树结构中,我们经常用到`Option`类型来表示节点的存在与否。例如,在插入和查找二叉树中,可以选择使用`if let`语句来处理`Some`和`None`的情况: +```rust +fn insert(&mut self, value: T) { + if let Some(ref mut node) = self.root { + node.insert(value); + } else { + self.root = Some(Box::new(TreeNode::new(value))); + } +} +``` +这种写法在处理可变引用时尤其简洁。 + +#### 4.2 排序算法与Ord与PartialOrd + +选择排序等算法需要比较泛型元素的大小,通常需要`PartialOrd`特性来支持部分排序(如非全序关系的情况),而对于要求全序的场景可以使用`Ord`。 + +#### 4.3 深度优先与广度优先搜索 + +在图算法中,深度优先搜索(DFS)和广度优先搜索(BFS)是两种基础的遍历方式: +- DFS示例: + ```rust + fn dfs_util(&self, v: usize, visited: &mut HashSet, visit_order: &mut Vec) { + visited.insert(v); + visit_order.push(v); + for &nei in self.adj[v].iter() { + if !visited.contains(&nei) { + self.dfs_util(nei, visited, visit_order); + } + } + } + ``` + +- BFS示例: + ```rust + fn bfs_with_return(&self, start: usize) -> Vec { + let mut visit_order = vec![]; + let mut visited = vec![false; self.adj.len()]; + let mut queue = VecDeque::new(); + queue.push_back(start); + visited[start] = true; + + while let Some(node) = queue.pop_front() { + visit_order.push(node); + for &neighbor in &self.adj[node] { + if !visited[neighbor] { + visited[neighbor] = true; + queue.push_back(neighbor); + } + } + } + visit_order + } + ``` + +#### 4.4 平衡堆的插入与调整 + +Rust标准库中`Vec`的`swap_remove`方法可以高效地删除指定位置的元素,适用于实现优先队列等堆结构: +```rust +let result = self.items.swap_remove(1); // 移除并返回指定位置的元素 +``` + +在删除元素后,可以通过调整堆结构(如最小/最大堆)来保持堆的性质。 + +--- + +### 5. 实现栈与队列 + +使用双队列实现栈的操作逻辑: +```rust +pub struct myStack { + q1: Queue, + q2: Queue +} + +impl myStack { + pub fn push(&mut self, elem: T) { + self.q2.enqueue(elem); + while !self.q1.is_empty() { + self.q2.enqueue(self.q1.dequeue().unwrap()); + } + std::mem::swap(&mut self.q1, &mut self.q2); + } +} +``` + +这种方法利用队列的FIFO特性来模拟栈的LIFO特性。 + +--- + +### 6. 函数与内存管理 + +Rust中的`Box`和`unsafe`结合用于手动管理堆内存。`Box::from_raw`可以从裸指针重新创建`Box`,这在需要手动内存管理的场景中非常有用。 +```rust +unsafe fn raw_pointer_to_box(ptr: *mut Foo) -> Box { + let mut ret: Box = unsafe { Box::from_raw(ptr) }; + ret.b = Some(String::from("hello")); + ret +} +``` + +这种方法常用于FFI(外部函数接口)中将指针恢复为拥有所有权的Rust类型。 + +--- + +### 总结 + +Rust语言通过丰富的内存管理工具和类型系统,确保了在安全性和性能上的平衡。无论是自定义数据结构还是排序、图遍历等基础算法,Rust的特性可以为代码提供极大的灵活性和安全保障。 \ No newline at end of file diff --git "a/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" "b/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" new file mode 100644 index 00000000000..030e6d890a1 --- /dev/null +++ "b/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" @@ -0,0 +1,7 @@ +--- +title: 2024秋冬季开源操作系统训练营第一阶段总结-wingrew +date: 2024-11-10 21:18:27 +tags: +--- + +该阶段练习rust语法。一百道题目够大家堪堪入门,数量正好。当然还有很多用法是后面在第二阶段慢慢掌握的。rust这门语言特色比较鲜明,所有权机制、生命周期管理等等,都挺有意思的,而且要和rustc斗智斗勇,很难忘。 diff --git "a/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" "b/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" new file mode 100644 index 00000000000..f38c8dfaa25 --- /dev/null +++ "b/source/_posts/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\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" @@ -0,0 +1,33 @@ +--- +title: 2024秋冬季开源操作系统训练营第一阶段总结报告-hxingjie +date: 2024-11-06 15:17:06 +categories: + - summary report +tags: + - author:hxingjie + - repo:https://github.com/LearningOS/rust-rustlings-2024-autumn-hxingjie +--- + + +## 一、前言 + + 在过去两周,我学习了Rust编程语言的相关知识。通读了一遍《Rust程序设计语言》书籍并完成了训练营的Rustlings练习。经过两周的学习,我对Rust有了初步的认识和掌握。以下是我对这两周学习过程的总结。 + + +## 二、学习内容 + +1. Rust基础知识 + +- Rust编程语言的基本语法,包括变量、数据类型、运算符、控制流等。 +- Rust的所有权系统,包括所有权、借用、生命周期等概念。 + +2. Rustlings练习 + +- 通过完成一系列练习,巩固对Rust基础知识的理解和应用。 +- 练习涵盖了Rust的所有权、借用、生命周期、错误处理、宏、模式匹配等方面的内容。 + + +## 三、学习心得 + + 非常感谢训练营的老师和助教帮助,让我能够在两周的时间快速入门rust这门优秀的语言。印象最深的还是最后的算法部份,尤其是前两道关于链表的题目,其实之前一直是使用c++做算法题,对链表也较为熟悉了,但是由于对rust的特性不熟悉以及对链表没有深刻理解,让我有一种有力使不出的感觉,后面通过阅读题目的框架,以及对书本知识的巩固,终于是对rust中的链表有了初步的认识,写完链表的题目后,后续的题目也很快完成了。rust语言的特性让我对编程和计算机有了更深的理解,尽管现在还是写得磕磕绊绊,和编译器打架,但是相信通过不断学习和时间,将来我也能够编写出优秀的rust代码。 + diff --git "a/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" "b/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" new file mode 100644 index 00000000000..44d97b65445 --- /dev/null +++ "b/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" @@ -0,0 +1,158 @@ +--- + +title: 调度与死锁 +date: 2024-11-11 +categories: + - os +tags: + - author:ekkure + - repo:https://github.com/ekkure/blog + +--- +## 调度 + +#### **三级调度:** + +作业调度、高级调度(频次最低):主要解决:接纳多少个任务+接纳那哪些任务这两个工作 + +进程调度、低级调度(频次最高): 必须有、**核心**,**确定哪个进程可以占有CPU并执行** + +中级调度:将那些暂时不能运行的进程从内存挂起到外存,(阻塞状态下进程实体(程序 + 数据 + PCB)还在内存中,而挂起状态会把进程实体挂到外存,但是PCB会存在系统内核空间中,会记录进程在外存的状态以及位置),一般在**内存紧张**时使用 + + + +高级调度,用于批处理系统中,将任务从外存调度到内存中去。(在分时/实时系统中,任务是直接在内存,因此没有高级调度) + +分时系统:**只有进程调度** + +批处理系统:**进程调度 + 作业调度** + + + +#### 调度算法相关 + +准则:周转时间(常用于**批处理系统**)、平均周转时间、带权周转时间 + +响应时间(交互性作业、分时系统)、截止时间的保证(实时系统)、优先权准则 + +周转时间 = 完成时间 - 到达时间 + +带权周转时间 = 周转时间 / 服务时间 + + +**调度算法** + +- FCFS(first come first serve), SJ(P)F等等 +对于抢占式调度,注意**服务时间的更新,然后再比较,看谁抢** + +- 高优先权 优先调度算法 + + 静态优先权:简单,但存在饥饿现象 + + 动态优先权:eg Rp = (等待时间 + 服务时间)/ 服务时间 作为优先权 1 + tw / ts; + +- 时间片轮转 ......? + + 多级反馈队列 S1 < S2 < S3 优先权 S1 > S2 > S3 + + + +- 实时调度 + + 非抢占:轮转 || 优先权 + + 抢占:基于中断时钟,好处是减少了上下文保存切换的次数 + + ​ 立即抢占 + + 实时调度算法:EDF、LLF,还有例题 + +- 其他一些?? + + MPS:CPU共享内存, 共享缓存(单个儿独立的,容易出现绑定,忙闲不均) + + SMP中进程分配方式:静态分配和动态分配 + + ​ 调度方式: 自调度和成组调度(两种方式就对应了用户级线程和系统级线程), 专用处理机分配? + + +## 死锁 + +**一些定义**: + +- 可剥夺资源:如主存,CPU,可以在使用时被强占的资源 +- 不可剥夺资源:不可被打断抢占的资源,如驱动器,打印机 +- 永久资源(外存),临时资源(进程运行过程中临时产生的数据资源等等) + +**竞争非剥夺资源,或者竞争临时资源可导致死锁** + +### 死锁的必要条件 + +- 互斥条件:进程互斥的使用临界资源 +- 不剥夺条件(不可抢占) +- 请求-保持条件:进程在申请新的资源的同时,保持对某些资源的占有 +- 环路等待:循环等待链 + + + + + +### 解决死锁的方法 + +从严格依次降低,为 + +预防 -> 避免 -> 检测与解除 + +#### 预防 + +上面4个条件是死锁的必要条件 , Deadlock -> 4 其逆否命题为 !4 -> !Deadlock,所以我们从4个条件入手 + +1. 互斥,并没有好的办法 +2. 不抢占:不抢占变成"抢占",如果进程申请不到全部资源时,主动释放 +3. 请求保持条件:使用AND机制,但是有点浪费资源 +4. 环路等待:破除环路,资源排序,参考哲学家进餐 + +#### 避免死锁 + +**这是比较中庸的做法,既不损耗很多的效率,也比较的严格** + +##### 银行家算法 + +一种是,资源分配表,为 + +| Process | Allocation | Need | Available | +| ------- | ---------- | ---- | --------- | +| | | | | + +另一种是,计算表 + +| Work | Need | Allocation | work + Allocation | Finish | +| ---- | ---- | ---------- | ----------------- | ------ | +| | | | | | + +**对资源进行分配时,分成两步** + +1. 判断分配请求 R是否满足 R < Available && R < Need +2. 如果满足1,使用表1表示分配后的资源表T1,再次计算是否存在安全序列,如果不安全,退回至T0,否则保存T1,下次分配将从T1开始。 + +#### 检测和解除 + +使用方法:**资源分配图** + +**几个结论** + +- 不可完全简化 => 存在死锁 +- 分配图中无环 => 不会存在死锁 +- 分配图中有环 => 不一定死锁 + +简化方法,对一个资源分配图,首先考虑持有边,如果持有者线程能够完成(获得所有需要的资源),将持有边消去后,将资源返回,如果不能完成,边消去后,仍保持资源占有,直到完成。 + +然后考虑请求边,如果请求的资源有空闲的,可以把边消去,若请求线程能够完成,则可将该资源返回,否则保持占有 + +重复上述过程,直至卡住,或者全部成孤立。 + +**解除** + +通过撤销进程或者挂起进程来释放一些资源,进而推动僵持状态。 + +而具体的对哪些进程,以什么样的顺序进行操作,可以参考`Dijkstra`之类的算法,找到一种损耗最小、利益最大的方法。 diff --git "a/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" "b/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" new file mode 100644 index 00000000000..a4ec5aaa6c4 --- /dev/null +++ "b/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" @@ -0,0 +1,9 @@ +--- +title: 2024秋冬季开源操作系统训练营第二阶段总结-wingrew +date: 2024-11-10 21:18:27 +tags: +--- + +本实验难度基本和os课程大作业难度一致,可能稍微容易一点。出于时间原因和个人能力不足,代码写得比较丑陋,健壮性不足。 + +第二阶段很好的带着大家窥探了os的几个基本部分。进程管理、内存管理、通信、文件系统等等比较好的串联起来了,能够让大家有连贯的感觉,知道os是怎么跑起来的,从教学代码来说,算是比较成功的,环环入扣。 diff --git "a/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" "b/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" new file mode 100644 index 00000000000..f415e1f2400 --- /dev/null +++ "b/source/_posts/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\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" @@ -0,0 +1,61 @@ +--- +title: 2024秋冬季开源操作系统训练营第二阶段总结报告-hxingjie +date: 2024-11-06 15:18:43 +categories: + - summary report +tags: + - author:hxingjie + - repo:https://github.com/LearningOS/2024a-rcore-hxingjie +--- + + +## 一、前言 + + 在过去两周,我学习了rCore操作系统。通过阅读实验指导书,我跟着操作系统的发展历程,学习了批处理系统、多道程序与分时多任务、虚拟地址空间、进程管理、文件系统、进程通信和并发等核心概念,并对rCore的实现有了更深入的认识。以下是我对这两周学习过程的总结。 + + +## 二、学习内容 + +1. 搭建执行环境: + + 学习了平台与目标三元组,理解了应用程序执行环境。 + +2. 批处理系统: + + 学习了批处理系统的基本原理,包括作业调度、作业执行过程。 + +3. 多道程序与分时多任务: + + 掌握了多道程序设计的基本概念,以及如何实现分时多任务调度。 + +4. 虚拟地址空间: + + 理解了虚拟内存的概念,包括页表、地址映射。 + +5. 进程管理: + + 学习了进程的管理、调度,更加深入的理解了fork+exec。 + +6. 文件系统: + + 掌握了文件系统的基本结构,包括目录、文件。 + +7. 进程通信: + + 学习了父子进程之间的通信机制——管道。 + +8. 并发: + + 学习了线程管理机制的设计与实现,理解了同步互斥的实现,包括锁、信号量、条件变量。 + + +## 三、学习心得 + + Rust语言的安全性和性能在rCore开发中得到了充分体现,经过阅读系统的源码以及作业的编写,对rust语言在内存安全和并发控制方面的优势有了更深的理解,第一阶段学习rust的很多疑问也得到了解答。 + + 通过学习rCore,我对操作系统的原理有了更深入的理解,虽然考研的时候较为系统的学习了操作系统的知识,但是基本上还是停留在理论知识方面。这次rCore学习之旅,我获取PCB对进程进行操作、实现课本上学习过的系统调用、深入汇编代码理解什么是 '陷入' ,让我对操作系统的设计理念、计算机的体系结构有了具象化的认识。 + + 在学习过程中,我也遇到了许多挑战,包括环境搭建遇到报错、Rust基础不够牢固导致代码编写举步维艰等,但通过不断解决这些问题,我的编程能力和问题解决能力得到了显著提升。 + + 两周的rCore学习之旅让我受益匪浅。通过学习rCore,我对操作系统的设计和实现有了更深刻的认识,同时也提升了我的编程技能。我相信,这些知识和经验将对我未来的学习和职业发展产生积极影响。 + diff --git "a/source/_posts/2024\347\247\213\345\206\254\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\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-lzh.md" "b/source/_posts/2024\347\247\213\345\206\254\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\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-lzh.md" new file mode 100644 index 00000000000..4072bcbbb1a --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\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\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-lzh.md" @@ -0,0 +1,35 @@ +--- +title: 2024秋冬开源操作系统训练营一二阶段总结 +date: 2024-11-10 22:39:56 +categories: + - 2024秋冬开源操作系统训练营 +tags: + - author:RedFlagLee + - repo:https://github.com/LearningOS/2024a-rcore-RedFlagLee + - rust + - rcore +--- +首先非常感谢训练营的主办方为我们提供了交流的平台和详细的文档,也非常感谢群里的助教和各位大佬们。作为一个已经工作了几年的人,如果没有这次训练营的机会,我很难相信自己能把操作系统重新捡起来并坚持学习。 + +## 第一阶段 + +这个阶段我主要是通过rustlings来零基础学习rust语法,主要参考的的书籍有《Rust圣经》和《Rust程序设计》。感觉rustlings有些过于简单,只能用来粗略地学习一下语法,通过了也谈不上熟练,我就在后面的项目编程中因此浪费了大量的时间去调试一下基础的rust语法问题,后期准备通过斯坦福的cs110l课程来加深下对rust在内存安全方面的理解。此外,在数据结构和算法部分我也主要靠chatGPT提供思路,很受打击,未来准备在leetcode上多刷下题来长长见识。 + +## 第二阶段 + +再次感谢文档的详细和版本划分的合理,本来之前停留在课本上的内存划分、进程调度、cache、并发等概念都变得触手可用,操作系统的迷雾总算被拨开了一角。 + +ch1里主要是摆脱了标准库依赖构建了祼机执行环境。通过qemu模拟器加载了最初版本的内核并在屏幕上输出了文字,我第一次感觉到了操作系统其实也和普通的应用程序一样,克服了畏难情绪。在这里也对第一性原理也了进一步的了解,再复杂的程序它的最初版本也是比较精简和易于理解的,从能完成最小功能的初始版本开始,会更有利于进一步学习其它更加抽象的概念。 + +ch2里的难点在于汇编知识和链接器的使用。我以前对于链接器的认识仅限于使用c++第三方库,但ch2里对于link_app.S的使用让我大开眼界,原来程序链接时每个段的处理可以这么灵活。同时,我也学到了在rust里通过extern c引入外部汇编文件定义的符号,可以直接拿到内存地址。我之前没有学过riscv架构,未来准备通过《RISC-V体系结构编程与实践》系统学习。 + +ch3里的难点在于汇编写的_switch在任务切换里的作用,例如保存寄存器、切换栈、切换控制流等。内核通过内嵌ecall汇编指令来引发trap异常陷入S特权级。抢占式调度里让我认识了时钟中断,原来轮转调度里的时间片就是通过定时器来触发时钟中断,进行任务切换。 + +ch4里内存地址空间我认为是最有趣也是最难的部分。通过为用户和内核单独实现的地址空间,解释了虚地址和物理地址的由来。为了实现地址空间,rcore里设计了大量的数据结构,重点要掌握MapArea和MemorySet里接口的使用,查找页表、生成地址空间等核心功能都在里面实现。 + +ch5里进程的精华主要在于fork和exec等系统调用的实现,本章还实现了一个shell程序,让我理解到了进程怎么从一个程序通过fork和exec运行其它程序的。 + +ch6里是文件的实现,ch8里是并发,我对这两章的理解不太深,主要还是围绕测试案例来理解的,后续还要再反复多次的看看文档。 + +现在非常期待下一阶段ArceOS的学习,希望能成为我入门hypervisor的阶梯!!! + diff --git "a/source/_posts/2024\347\247\213\345\206\254\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\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-FunCheney.md" "b/source/_posts/2024\347\247\213\345\206\254\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\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-FunCheney.md" new file mode 100644 index 00000000000..0d0b074af7a --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\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\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-FunCheney.md" @@ -0,0 +1,43 @@ +--- +title: 2024秋冬开源操作系统训练营第一二阶段总结-FunCheney +date: 2024-11-11 07:40:28 +categories: + - 2024秋冬季季开源操作系统训练营 +tags: + - author:FunCheney + - repo:https://github.com/LearningOS/2024a-rcore-FunCheney + - ... +--- + +### 第一阶段 + +之前在公司工作的时候就了解到了 rust,工作中客户端使用 rust,当时觉得 rust 离后端(服务端)比较远,就错过了 rust 的深入了解与学习。 +在这几年的工作与学习中,慢慢接触到rust在操作系统的领域也在使用。于是准备学习一下这门新的语言。自己也想对操作系统相关方面做一些 +更加深入的了解。 + +一次偶然的机会在github 上看到了 rcore,用 rust 写操作系统,这个和自己的需求完美契合了。。。 + +本课程第一阶段采用 rustlings,在线测评,然后对应的知识点也会有出处。是一个非常好的学习工具,但是整体来说数据结构部分相对较难,需要 +一些相关数据结构的基础,以及对 rust 语法的了解。 + +自己对 rust 相关的一学习的例子与记录: https://github.com/FunCheney/Fos/tree/main/rust-study + +整体感觉: +1. 语言的学习还是要多动手练习,多写代码才能理解 +2. 数据结构与算法相关的章节还需要持续的练习 +3. 后续还要安排并发相关的学习与记录:https://github.com/FunCheney/Fos/tree/main/rust-study/rust-atomics-and-locks + +### 第二阶段 + +整体来时实验教程偏简单且大多都一笔带过。学习的过程中主要还是参考: rCore-Tutorial-Book 第三版! + +因为在训练营开始之前我就已经在通过看: rCore-Tutorial-Book 以及抄 https://github.com/rcore-os/rCore-Tutorial-v3 中每一章节相关的代码, +因此在训练营阶段也在抄其中代码,在自己的仓库里面手写了一遍相关代码。 + +对 risc-v 精简指令架构有了一些了解:https://github.com/FunCheney/Fos/tree/main/code/asm 主要代码实现。 +整体感觉: +1. risc-v 的指令架构没有 intel x86 指令复杂。 +2. 文件系统,与锁相关的实验部分用时教程,偏难。 +3. rust-sbi 和 qemu 会简化很多汇编的相关的工作。降低了对操作系统启动记载相关的一些理解门槛,之间看过 linux 0.11 相关的启动逻辑,还是比较复杂。 +4. 通过在线测评的方式,通过一些测试用例可以起到检测的收手段。比自己写代码的时候(尤其是还在学习过程中)不知到对错会更有方向。但是还是缺少一些 + 最佳的实现知道,因为不知道自己过了是否就是好的编码方式,好的实现思路。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\270\200\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" "b/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\270\200\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" new file mode 100644 index 00000000000..57b455584cd --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\270\200\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" @@ -0,0 +1,21 @@ +--- +title: 2024秋冬训练营Blog-阶段一总结-孙宇航 +date: 2024-11-11 12:55:19 +tags: +--- +# Rustlings总结 +## 心路历程 + 这个训练营我也算是“老兵”了,不断地入门再入门,到终于下定决心,投入大量的时间来完成这个训练营,真的经历了很多很多,有着太多的心路历程,从想着三天写完,慢 +慢的查漏补缺,温习之前的知识点,慢慢的开始一道题一道题手写rustlings... + 我总是说我基础很差,并拿这句话当做挡箭牌,最终一事无成。未来会更好吗?但是我已经有点厌倦痛惜过去了。 +说起来这也是我第三次的写rustlings了,开始越来越熟练了,这当然是开心的,不过后面有这更多的更艰难的挑战,阶段二,阶段三,都是未知的大山,更艰难的挑战。 +## 学习由来 + 对于这个训练营,我是大二机缘巧合开始接触到的,那时候听学长说,特别有含金量,可以“咸鱼翻身”,那可是清华啊,多么具有神话幻想意味的大学,那时的我突然想着, +我一定能做出一番令人羡慕的成就。 + 但是那时的我用两个字来概括是“摆烂”,在羡慕同学取得的成就和奖项,和自己什么事也不做只打游戏的情况下还希望着未来自己能有一番大成就,如今看来竟全是一片混 +浊,在孤独和幻想中,我度过了大学的大半时光,我的性格是卑劣的。 + 但是未来的路仍旧一片黑暗,载着家人的希望,我在原地自顾自的打转,跳不出自己的镣铐,做不到的仍然做不到,幻想的事物越发离谱。 + 慢慢得走来,我只剩了这个训练营了,又是另一个未完待续。 + 我可以做到吗?我问过了很多人,很多未曾谋面的人,陌生的人对我的态度竟大都是积极的,他们不了解我。我也正在逐步了解自己。忘记了太多的事情,记忆也越来越差,但 +是从哪个角度来说,我这次真的想完成这次训练营,即使我每次都这么说... + 祝武运隆昌。 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\272\214\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" "b/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\272\214\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" new file mode 100644 index 00000000000..5672e95b227 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\272\214\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" @@ -0,0 +1,10 @@ +--- +title: 2024秋冬训练营Blog-阶段二总结-孙宇航 +date: 2024-11-11 12:55:40 +tags: +--- +# 专业阶段 +这个阶段给我最大的感受是学习,因为很多函数都被事先书写好了,这几个实验基本都是在已有的框架上进行相关的函数调用即可,在明白每个函数的实现逻辑以及每个lab实验要实现的功能即可完成实验。同时这个阶段给我的感受是有许多深层次的知识仍需要学习,有许多一知半解的知识需要实践,看到学习群里面大家对于实验的讨论的诸多见解,受益匪浅,完完全全的拓宽了我的眼界,许多从未听过的名词出现在我的面前,只感到纸上得来终觉浅,绝知此事要躬行。可惜时间所迫,没办法对于每个lab实验进行进一步的深究。 +第二阶段我重点理会到了操作系统的一步步发展以及实现的功能,在进行每个实验的时候,感受到学校里面的讲解十分片面,并且浅薄(也是我没有认真学习的缘故,学校里面的课检验的只有期末突击而已)。 +非常重要的一点是这次的理论和实践一起进行,让我极大的认识到了抽象和具体之间的联系,有时候理论上很复杂并且难以理解的东西,转化到实践上面竟然可能只是一个数组,一个函数栈而已,这让欠缺实践的我大开眼界。 +非常期待第三阶段带来的挑战,也非常希望可以通过第三阶段(哈哈哈)。 \ No newline at end of file diff --git a/source/_posts/hkp-blog.md b/source/_posts/hkp-blog.md new file mode 100644 index 00000000000..083e23cf288 --- /dev/null +++ b/source/_posts/hkp-blog.md @@ -0,0 +1,65 @@ +--- +title: hkp_blog +date: 2024-11-10 20:38:39 +tags: + - author:贺琨鹏 + - repo:https://github.com/LearningOS/2024a-rcore-nakedhkp +--- + +# 第一阶段的总结 +1. 初识 Rust:语法与基础 +Rust 的语法相比其他系统编程语言更为严谨,并且采用了一些新颖的设计,比如所有权(Ownership)和借用(Borrowing)模型。通过学习变量、函数、控制流、数据结构等基础内容,我逐步熟悉了 Rust 的编程风格。同时,Rust 强制规定的编译检查(如变量不可变性、类型检查)帮助我在早期阶段就养成了良好的编程习惯。 + +2. 内存管理与所有权 +Rust 的核心设计之一是其所有权系统,这也是区别于其他语言的最显著特性之一。在 Rust 中,每个变量都有一个所有权(Owner),变量的作用域结束时会自动释放资源,无需手动管理内存,这有效地避免了悬空指针和内存泄漏问题。在学习过程中,我对以下几个概念有了深刻理解: + +所有权(Ownership):每个数据在同一时间只能有一个所有者。 +借用(Borrowing):通过引用传递数据,允许在不改变所有权的情况下访问数据。 +生命周期(Lifetimes):通过生命周期标注来管理数据的引用,使得引用的使用更加安全。 +所有权系统让 Rust 在没有垃圾回收的情况下也能有效管理内存,学习这些概念后,我对资源管理的意识有了显著提升。 + +3. 错误处理 +Rust 的错误处理也与众不同。相比传统语言使用的异常机制,Rust 使用 Result 和 Option 枚举来处理错误和可选值。这种显式的错误处理方式让我更加清楚地理解了每个操作的潜在风险,并培养了防御性编程的习惯。此外,通过使用 ? 操作符,代码变得简洁易读,让错误处理不再显得冗长。 + +4. 并发编程 +Rust 在并发编程上的独特设计使其特别适合编写高性能的并发应用。Rust 的所有权模型通过在编译期检查数据竞争,有效防止了多线程中的常见问题,比如数据竞争、死锁等。Rust 提供的 std::thread、async/await 异步编程模型等工具使并发编程更加安全且易于管理。在学习中,我掌握了创建线程、共享数据的方式,并学会使用 Mutex 和 Arc 等工具来处理复杂的多线程情况。 + +5. Cargo 和生态系统 +Rust 的包管理器和构建工具 Cargo 是 Rust 生态系统的重要组成部分。在学习中,我逐步熟悉了如何使用 Cargo 创建项目、管理依赖、构建和测试代码。Rust 拥有活跃的社区和丰富的库资源,通过使用第三方库,可以快速实现许多功能,这大大提升了我的开发效率。 + +6. 实践中的收获 +理论学习之后,我通过一些实际项目(如简单的 Web 服务、命令行工具)将所学知识应用于实践。Rust 对类型和内存的严格检查在初期可能显得繁琐,但这些设计让我更清楚地理解代码结构,减少了运行时的错误。实践过程中,我逐渐感受到 Rust 的优势:高性能、可靠性和低级控制,使得 Rust 成为编写高效、安全应用的理想选择。 +# 第二阶段总结 + +在第二阶段的学习中,我逐步深入理解了操作系统中各个功能模块的实现。这一阶段的主要任务是通过阅读和理解代码的层次结构,来掌握系统的整体设计。在初期,我对项目代码结构的理解较为模糊,但随着长时间的代码阅读和实践,我总结出了一些行之有效的阅读和理解方法: + +1. **分层次理解代码**:我发现从高层抽象入手去理解代码结构更为高效。首先理清最高层的抽象层次,理解其核心功能,再逐渐深入到底层实现的细节。在实验中,掌握和利用高层抽象往往能够帮助节省大量时间。 + +--- + +### 实验总结 + +#### 实验一:基础系统调用实现 + +实验一的难度较低,因为此阶段还未实现页表管理,内核可以直接访问用户态的地址空间。实验中,系统调用的返回值可以直接使用内核中的数据。此外,通过高精度的 `get_time_us` 函数获取时间,能够确保后续实验中通过测试。为实现系统调用计数,可以在 `syscall` 函数调用时更新计数,而首次调用时间则需在 `run_first_task` 和 `run_next_task` 中进行设置。 + +#### 实验二:引入页表管理 + +在实验二中,由于实现了页表管理,系统调用设计需要改写。内核在处理传入参数时需找到实际的物理地址,并在该地址上读写数据。同时,本次实验引入了两个新的系统调用 `mmap` 和 `munmap`。因为两者涉及一段连续地址,因此在设计中需确保连续地址的映射状态(已映射或未映射)。最后,`mmap` 和 `munmap` 分别负责插入和删除页面。 + +#### 实验三:进程管理与调度算法实现 + +实验三的任务是实现 `spawn` 系统调用和 `stride` 调度算法。`spawn` 不能简单地视作 `fork + exec` 的组合。在实现前需理解 `spawn` 的语义,然后从 `fork + exec` 中提取相关代码,使得 TCB 数据结构达到所需的状态。`stride` 是一种进程调度算法,实现较为简单,按照教程提供的步骤逐步操作即可。 + +#### 实验四:兼容性与文件系统操作 + +实验四相较之前难度有所提升,需要保证现有代码的兼容性。一开始 `spawn` 存在兼容性问题,导致错误频发,最终通过重新实现 `spawn` 解决了兼容问题。此外,死锁处理是本实验的重点之一。由于某些函数(如 `find` 和 `create`)对文件系统上了锁,因此这些函数不可直接调用,但可以通过拼接部分代码来实现所需的功能。`unlink` 目录项的删除操作则需更新目录项,实验中借鉴了其他同学的思路,通过删除原内容并复制新内容实现目录项更新。 + +#### 实验五:死锁检测算法实现 + +实验五主要任务是实现死锁检测算法,该算法类似于银行家算法。教程中的描述较为简略,许多细节需要在实现中仔细推敲。`sem` 和 `mutex` 的实现大致相同。此外,`sys_get_time` 必须实现,否则某些实例可能会引发死锁。 + +--- + +### 整体总结 +本阶段实验的概念和难度相对基础,更多的挑战在于使用 Rust 编写操作系统。对于首次接触 Rust 的我来说,这种体验充满新奇,同时也加深了我对 Rust 的理解。Rust 强大的内存安全和所有权模型,在系统级编程中尤为适用 diff --git "a/source/_posts/liguanxiao-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" "b/source/_posts/liguanxiao-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" new file mode 100644 index 00000000000..a2a65e2342b --- /dev/null +++ "b/source/_posts/liguanxiao-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" @@ -0,0 +1,19 @@ +--- +title: liguanxiao-第一二阶段总结 +date: 2024-11-11 11:58:03 +tags: +--- + +第一阶段: + +比较简单非常方便新手入门,可以很好的对rust的学习曲线难度经行拟合. + + + +第二阶段: + +由于研究生的忙碌确实让我有感觉到力不从心, + +但是课程比较有用 我之前是干java的对于设计模式和代码规范有一定的追求所以希望在第二阶段对这方面补充 + +实操教学真的很好 不仅可以检验知识点也可以检验代码能力两不耽误 \ No newline at end of file diff --git "a/source/_posts/mtul-rcore-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" "b/source/_posts/mtul-rcore-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" new file mode 100644 index 00000000000..f093ec4c511 --- /dev/null +++ "b/source/_posts/mtul-rcore-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" @@ -0,0 +1,130 @@ +--- +title: mtul-rcore-第一二阶段总结 +date: 2024-11-11 22:22:14 +tags: +--- +## 第一阶段 - rust基础与算法 + +由于我有rust基础,没有遇到什么困难,但还是第一次完整地做一次rustlings,有一定收获 + +## 第二阶段 - 专业阶段 + +### 实验环境配置 + +rcore开发的环境配置比较繁琐,而我恰好比较熟悉nix,因此我用nix定义的一个完美兼容rcore的开发环境。由于不需要 `rustup` , 必须在 `os/Makefile` 中禁用 rust 环境的检测. `flake.nix` 如下: + +```nix +{ + description = "rCore dev flake"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + oldnixpkgs.url = "github:NixOS/nixpkgs/7cf5ccf1cdb2ba5f08f0ac29fc3d04b0b59a07e4"; + flake-utils.url = "github:numtide/flake-utils"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + nixpkgs, + oldnixpkgs, + flake-utils, + rust-overlay, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [(import rust-overlay)]; + }; + oldpkgs = import oldnixpkgs { + inherit system; + }; + in { + devShells.default = pkgs.mkShell { + packages = with pkgs; + [ + (rust-bin.nightly."2024-05-02".minimal.override { + extensions = [ + "rust-src" + "llvm-tools" + "rustfmt" + "rust-analyzer" + "rust-docs" + "clippy" + ]; + targets = ["riscv64gc-unknown-none-elf"]; + }) + cargo-binutils + python3 + gdb + tmux + ] + ++ [oldpkgs.qemu]; + + # 进入环境后显示rust和qemu版本 + shellHook = '' + rustc --version + cargo --version + qemu-system-riscv64 --version + qemu-riscv64 --version + ''; + }; + }); +} +``` + +这份 `flake.nix` 也已经分享到[官方问答论坛](https://opencamp.cn/os2edu/bbs/1391) + +### 第一/二章 + +阅读这两章需要先了解riscv,特别是特权级相关设计。 + +主要参考: + +- [RISC-V开放架构设计之道][1] +- [The RISC-V Reader: An Open Architecture Atlas][2] 前一个的英文原著,而且有能下载到单独的RISC-V Green Card,方便查阅 +- [RISC-V Instruction Set Manual][3] 完整详细 + +### 第三章:多道程序与分时多任务 + +学习了系统调用,陷入等知识,上下文切换过程中通用寄存器和CSR的使用加深了我对riscv特权级设计的理解。 + +本章练习较为简单。 + +### 第四章:地址空间 + +SV39的设计又引入了若干相关的寄存器,如satp, pmp csr。查阅[riscv manul][3]以加深理解。 + +本章练习中,为了处理*请求映射已经被映射的页*的错误,我使用了`Result`错误传递,无法想象如果不使用`Result`和`?`语法糖我的代码会多么丑陋。然而很奇怪,整个rcore中极少使用`Result`。 + +### 第五章:进程及进程管理 + +本章内容比较轻松,完善了TCB的设计并实现`fork()`和`exec()`系统调用。 + +本章练习也比较简单。 + +### 第六章:文件系统 + +easy-fs is **NOT** easy!层层抽象几乎让我晕头转向! + +尽管如此,easy-fs囊括了superblock、索引节点、blockcache等现代文件系统中的基础概念,似乎不能再精简了。 + +link和unlink操作主要是查找inode并创建/删除目录项。在inode_block里创建与删除目录项无非是一些线性序列的操作,但由于没有封装成`&[DirEntry]`,需要手动操作,比较费劲。将来有空我会尝试改进一下。 + +### 第七章:进程间通信 + +本章内容较少,但进程间通信是个大话题,还需拓展学习。 + +### 第八章:并发 + +学习了多线程的同步与互斥。 + +练习:学习了死锁检测算法 + +[1]: https://ysyx.oscc.cc/books/riscv-reader.html +[2]: http://www.riscvbook.com/ +[3]: https://github.com/riscv/riscv-isa-manual + diff --git "a/source/_posts/rcore-2024-a\344\270\200\344\272\214\351\230\266\346\256\265\345\256\236\351\252\214\346\200\273\347\273\223.md" "b/source/_posts/rcore-2024-a\344\270\200\344\272\214\351\230\266\346\256\265\345\256\236\351\252\214\346\200\273\347\273\223.md" new file mode 100644 index 00000000000..f873425557d --- /dev/null +++ "b/source/_posts/rcore-2024-a\344\270\200\344\272\214\351\230\266\346\256\265\345\256\236\351\252\214\346\200\273\347\273\223.md" @@ -0,0 +1,27 @@ +--- +title: rcore-2024-a-一二阶段实验总结 +date: 2024-11-11 16:00:00 +tags: + - author: shengdaozm +--- + +紧赶慢赶,终于是在交上了最后的实验,带着许多想法,写下了这篇总结。同样作为清朝老兵,我又回来了。 + +## 一阶段:Rust 110 道算法题 +在第一阶段,主要通过编写 110 道算法题,掌握 Rust 编程的基本和高级特性。Rust 语言强调安全性和高效性, +这在算法编程中尤其重要,是的,单个语法题目还是比较轻松的,查阅资料什么的也都可以直接解决。当真正开始算法部分的 +题目的时候,才开始慢慢体会到rust的折磨——借用检查器(Borrow Checker)、所有权(Ownership)以及生命周期(Lifetime)等概念 +统统在处理复杂数据结构时痛击我,Rust 的强类型系统帮助我减少了运行时错误,经常是我的逻辑天衣无缝,但还是borrowed error。 +没事,慢慢折磨吧,折磨多了就会了。。。 + +实际上,作为清朝老兵,一阶段倒是没花多少时间,翻出以前的仓库,看看填填也就过去了,但是当时熬夜做rustlings的日子仍然是我挥之不去的记忆。酸爽! + +## 二阶段:实现 rCore 简单内核 + +是的,一拳打爆rcore。第二阶段则是操作系统开发,说白了就是补全各种系统调用。我也慢慢感受到书上说的种种在我看起来毫不起眼的东西,真正实现起来是真的困难,(这里点名虚拟内存和文件系统,东西是真的很多),每个ch都是一脸懵到嘎嘎乱写,然后对着panic疯狂调,期间甚至一度动摇额我的计算机世界观——计算机是真的有玄学啊。不过还好,总是能在某些神奇的地方调调代码就神奇地通过了测试,也是给我留下了不少的未解之谜。 + +在二阶段确实收获了不少,我现在对于一些较大的工程项目已经没有感觉了,也确实是慢慢熟悉rust了,好玩,爱玩!期待后面的考核。 + +祝训练营越办越好!希望能在虚拟内存和文件管理那多讲一点,确实很抽象。 + +最后,感觉你读完我的碎碎念,不管怎样,还是磕磕绊绊弄完了这两个阶段,也算是给上半年自己中间跑路一个交代吧,行文至此,拜拜。 \ No newline at end of file diff --git "a/source/_posts/sevetis-\344\270\200\344\272\214\351\230\266\346\256\265blog.md" "b/source/_posts/sevetis-\344\270\200\344\272\214\351\230\266\346\256\265blog.md" new file mode 100644 index 00000000000..8ffefe9d74d --- /dev/null +++ "b/source/_posts/sevetis-\344\270\200\344\272\214\351\230\266\346\256\265blog.md" @@ -0,0 +1,26 @@ +--- +title: sevetis-一二阶段blog +date: 2024-11-11 14:52:02 +tags: +--- + +## 前言 +- 一个大三学生, 偶然看到这个学习活动就来玩一下了. 之前学校操作系统课学了个寂寞, 正好来学多点补全知识, 并且练练rust. + +## rustling +- 这个还好, 以前有点点rust基础. 做着没什么困难, 顺便查漏补缺了一下. + +## rcore +- 二阶段开始, 内容一下多了起来, 文档看着也挺枯燥的, 有些地方感觉写得对小菜菜不太友好QAQ. +- lab1 + - 翻翻文档, 翻到了ch3. 懒得再细看文档了, 直接写代码做实验!看了一下要求, 直接加点东西就一次过了, 开心. +- lab2 + - ch4这个卡了我两周, 内存虚拟化这块真的很不会, 做着不明不白. 有点想摆烂了, 看了看群里聊天记录(光是看群聊信息都能学很多), 有点思路, 往大方向试了一试, 通过.(可能通过了还是不太明白) +- lab3 + - ch5不是很难, stride调度算法出了点小错误稍微卡了一下, 很快就过了. +- lab4 + - 听很多人说ch6最难. 文件系统我也不太会,但我认真看了文档和代码, 试了几次也过了, 没感觉太难. +- lab5 + - 做得最痛苦的实验. 这个实验主要实现死锁检测, 本身不是很难. 但我遇到了一些玄学问题, 有`sleep_blocking`的测例会在sleep卡住超时过不了, 找来找去找不到原因, 加上DDL到了, 心态小崩. 最后垂死挣扎, 卸了qemu9, 把它换成qemu7, 不怎么抱期望地跑了一下, 通过了!(据说有一些人用qemu9也可以通过, 但不知道为什么我不行.) + +#### 总的来说, 第二阶段学到了很多东西, 以前漏掉没学的操作系统知识被补上很多, 但更感觉有更多的东西要学. 同时有时做实验遇到困难怎么尝试都通过不了, 最后再坚持一下通过了, 很有成就感.(感觉实验测例有点简单..) diff --git a/source/_posts/stage-1-ProerLoneW.md b/source/_posts/stage-1-ProerLoneW.md new file mode 100644 index 00000000000..dc21287694b --- /dev/null +++ b/source/_posts/stage-1-ProerLoneW.md @@ -0,0 +1,26 @@ +--- +title: stage-1-ProerLoneW +date: 2024-11-13 21:17:20 +categories: + - report +tags: + - author: ProerLoneW + - repo: rust-rustlings-2024-autumn-ProerLoneW + - rust-learning +--- + +# rCore第一阶段总结报告 + +### 参加训练营的契机 + +​ 本人是大三信息安全专业学生,起因是在学期初老师在班级群中转发了本次训练营的相关推送,正好本学期在修操作系统专业课,再加上在过去的两年实际上没有学过过硬的技术,就希望能通过本次训练营收获到许多实际项目开发相关的东西,希望能做到技多不压身。 + + + +### 第一阶段回顾 + +​ 本阶段的主要任务是根据 `rust语言官方圣经` 来学习rust语法,并在简单的知识训练、算法实现中掌握基本的编程逻辑和rust语言的独特思维。在学习过程中最大的感受就是,这是一门非常“麻烦”(或者说拗口)、非常底层但却非常“安全”的语言。 + +​ 在进一步地阅读文档和代码实操的过程中,我也深深地体会到了rust语言的魅力,很多在其他语言中不需要关注的问题,都是rust使用者的家常便饭,例如所有权与借用、生命周期、模式匹配、并发和线程、内存管理、特征泛型、智能指针、宏等等全新的概念,在学习的过程中,也能十分自然地和以往所写过的语言进行对比,也能十分自然地联想到自己所学的专业知识,整个学习过程是痛苦而充满趣味和成就感的。 + +​ 最终也是断断续续地在国庆后完成了110分的编程题目,虽然很多地方都有囫囵吞枣、只求大概的不好处理,但还算是初步打开了rust语言的大门。 diff --git a/source/_posts/stage-2-ProerLoneW.md b/source/_posts/stage-2-ProerLoneW.md new file mode 100644 index 00000000000..da479c0c431 --- /dev/null +++ b/source/_posts/stage-2-ProerLoneW.md @@ -0,0 +1,43 @@ +--- +title: stage-2-ProerLoneW +date: 2024-11-13 23:33:51 +categories: + - report +tags: + - author: ProerLoneW + - repo: 2024a-rcore-ProerLoneW + - os-first-step +--- + +# rCore第二阶段总结报告 + +### 第二阶段回顾 + +​ 本以为第一阶段后将是一马平川,却不曾想第二阶段竟是噩梦的开始。本以为第二阶段也和第一阶段一样,只需断断续续抽出一周的零碎时间即可轻易完成,但只有亲身尝试过才会知道这种想法多么的错误,最后几乎是推掉了所有的学业任务,把一整周都投入在了rCore上才勉勉强强卡ddl写完了所有lab。 + +​ 第零章和到第二章可以说是第二阶段的环境配置和任务理解阶段,由于上一阶段仅仅是在mac电脑上轻松写代码,故在一开始的环境配置上还是耗费了小两天,在此过程中第一次接触到了windows的wsl,然后一步一步在实验指导书的指导下搭建了 `vscode + wsl + ubuntu20.02` 这样的开发环境,在阅读前面几章的文档内容后也对所学的知识、实验的相关方向有了大致的了解,并能够初步运行起来自己的rust-os。在第一章的学习过程中,我理解了应用程序的执行环境,对 `应用程序、函数调用、标准库、系统调用、操作系统、指令集和硬件平台` 这些概念又有了新的认识,有种学习汇编语言的感觉,另外也接触到了 `裸机编程、目标三元组` 这些比较新的东西。但也仅停留在有印象的层面,没能深入理解其中奥秘;第二章的内容比较全面,我了解到了应用程序执行过程和操作系统的特权级切换机制,了解了编写risc-v的链接脚本来控制内存布局、内嵌汇编、异常处理、上下文切换的实现,这些操作在代码中的实现,更是让我操作系统的课上所学映入了现实,第二章的批处理调度系统,也是一个很好的帮助我入门并理解整体的代码架构的案例。 + +​ 后面几章就没有那么多时间细细咀嚼了,通常都是扫两眼知识然后直奔作业,除了文件系统外,其他由于都在课上学过,因此在gpt的帮助下没有被卡住太久的时间。也很感谢用心设计该实验教程的学长/学姐,不仅让我们快速入门了os,还让我们快速了解了如何系统开发一个os的各个功能。 + +​ 在lab1实验就卡了很久——不会仔细品读知识中所蕴含的代码实现细节,也不会投机取巧地去看看测试用例和作业所给的提示,而仅仅是闭门造车,最终卡了许久才在群聊天记录中找到了问题的关键所在,也就是时间的记录问题,当然在写的过程中也遇到诸如生命周期等等问题,让语言基础不太牢固的我举步维艰。 + +​ lab2是虚拟存储多级页表实验,虽然在课上老听说页表的神奇和重要性,但从没有像本次实验这样深刻地接触过操作系统中的页表,最初做的时候由于考虑的太多又无法实现便导致一度停滞不前,后来在发呆的时候又仔细重新阅读了一下题目的要求,发现需要实现的东西都还挺简单的,而且测试用例也给的非常宽松因此很快的做完了,并没有想象中那么复杂。 + +​ lab3是有关进程的简单创建和调度,实现上并不困难,主要难度还是在于代码结构发生了较大变化,比如本来 `task_manager` 做的事情现在换成了 `process` 在做。 + +​ lab4是最痛苦的一次实验,在把ch5的代码移植过来后发现仅需要过三个测试文件即可便觉得它很简单,但真正想要得心应手地写出来需要对文件系统和代码实现有详细的理解,最终还是在听了ddl前的讲解才恍然大悟:linkat和create略像前一章所提到的fork和spawn,否则将根本无从下手然后白白浪费时间并放弃之前的一切努力。在给 `inode` 加上 `inode_id` 相关的方法后很快完成了这次实验。 + +​ lab5的内容相对比较熟悉,也是课上自认为十分简单的死锁检测问题,但代码框架阅读起来难度较大,最终将前面的 `sys_get_time` 搬过来后跟着题目的提示实现了资源分配图和死锁检测系统调用的实现 + + + +### 第二阶段收获 + +​ 这次的第二阶段学习就像一场不断挑战极限的马拉松,让我既感到疲惫不堪,又充满了意想不到的收获。在本阶段的学习中,我获得了关于操作系统核心机制的更深入理解,体验到了真实操作系统开发的复杂性和细致入微的编程要求,特别是在进程管理、内存管理、以及文件系统的设计上有了全新的认识。 + +​ 首先,通过批处理调度系统的实现,我理解了特权级切换、上下文保存与恢复等机制。这种直接操控硬件资源的编程体验帮助我更好地理解了操作系统在管理硬件与应用间的角色。其次,在实现文件系统的过程中,我也是初次了解了文件路径解析、inode 管理和文件描述符等底层概念,这不仅让我理解了文件系统的设计精髓,也磨炼了抽象和模块化编程的能力。 + +​ 同时,在死锁检测的实验中,资源分配图的设计让我更深刻地理解了进程间的依赖关系和并发控制策略,学习知识的最好方式绝对是动手实践。 + +​ 总的来说,这一阶段的收获不止是技术上的进步,更让我体会到了系统开发的全局性思维和精确性要求。这不仅提升了我的编程能力,也让我更有信心面对后续操作系统开发中的复杂问题。 + diff --git "a/source/_posts/\345\217\256\345\275\223\347\214\253\347\232\204\345\215\232\345\256\242.md" "b/source/_posts/\345\217\256\345\275\223\347\214\253\347\232\204\345\215\232\345\256\242.md" new file mode 100644 index 00000000000..991a87e09ed --- /dev/null +++ "b/source/_posts/\345\217\256\345\275\223\347\214\253\347\232\204\345\215\232\345\256\242.md" @@ -0,0 +1,20 @@ +--- +title: 叮当猫的博客 +date: 2024-11-11 03:42:49 +categories: + - report +tags: + - author:Ding-dangmao +--- + +# 一阶段 Rustlings + +​ 机缘巧合之下,对我教育颇深的学长为我介绍了这个训练营,于是一段艰辛的历史就开始了 + +​ 初学rust,被他严格是语法体系给搞傻了,这也不给那也不给,对于写惯C++ 的我来说简直不可理喻。rust语法体系中不允许隐式类型转换,即使是在C++中的非窄化类型转换也不允许。更要命的一点是,所有变量默认全是按const不可变变量来处理的,这极大地降低了我的愉悦性,在C++中此类const操作时显示的,在这变为隐式。还有一点则是类型的特性不会自动从父类继承(C++是这么称呼的,rust学的不太行),必须我去一 一 写出,也是很难受。 + +​ 介绍完了令我不愉快的地方,rust的优势也很明显,不允许随意更改变量,不允许直接操作指针,变量的所有权等等,在重重限制下无疑降低了代码出错率,不过我还是喜欢C++。 + +# 二阶段rcore + +​ 这一阶段别提多痛苦了,一阶段语法就没学好,大半时间都在调语法错误,章节知识点介绍都挺好的,想一想也容易想出来,over,不过我还是喜欢C++,rust使我痛苦 , 重复可变借用我恨你