rustlings 总结
Rustlings 是学习 Rust 编程语言的极佳练习工具,它包含了多个由浅入深的练习题目,帮助学习者快速掌握 Rust 的基础知识和重要概念。
1. 变量与可变性
- Rust 中的变量默认是不可变的(immutable),即变量在声明后无法更改。要让变量可变,必须显式添加
mut
关键字。 - 这种默认不可变性帮助开发者避免无意的状态变化,提高代码的安全性和可维护性。
2. 数据类型
- Rust 是静态类型语言,编译器会在编译阶段检查数据类型。
- Rust 支持多种数据类型,包括标量类型(整型、浮点型、布尔型、字符)和复合类型(元组、数组等)。
3. 所有权机制
- Rust 的所有权系统是其内存安全性和性能的重要保障。
- 每个值在同一时间只能有一个所有者,当所有者变量超出作用域时,内存会自动释放。所有权的转移、借用和引用(可变和不可变)是理解 Rust 内存管理的关键。
4. 借用与引用
- 借用(Borrowing)允许在不转移所有权的情况下使用数据。
- Rust 有严格的借用规则:在同一作用域中,只允许一个可变引用或多个不可变引用,确保内存安全。
5. 结构体与枚举
- 结构体(Struct)用于将不同的数据组合成一个复合类型,枚举(Enum)用于定义一组可能的状态或值。
- Rust 的枚举非常强大,支持绑定数据,并且可以与模式匹配一起使用,帮助更清晰地处理复杂的逻辑分支。
6. 模式匹配
match
表达式和if let
是 Rust 中处理分支的主要工具,尤其是当处理枚举和结果类型(Result
)时。match
语法不仅简洁,还能避免遗漏某些分支,确保代码的健壮性。
7. 错误处理
- Rust 提供了
Result
和Option
类型来进行错误处理和空值处理。 - 使用
unwrap
、expect
、match
等方式处理这些类型,开发者可以编写出更健壮的代码,避免程序在运行时崩溃。
8. 所有权的移动与复制
- 移动(Move):当变量的所有权被转移时,源变量将不可用。
- 复制(Copy):对于实现了
Copy
特征的类型(如基本数据类型),赋值不会转移所有权,而是直接复制。
9. 特征
- Rust 中的特征类似于其他语言的接口,用于定义一组方法签名,供结构体或枚举实现。
- 特征使得 Rust 支持多态,通过泛型和特征约束实现代码的复用和接口一致性。
10. 智能指针
- Rust 的标准库中提供了
Box
、Rc
和RefCell
等智能指针类型,帮助管理内存和共享数据。 Box
实现堆分配,Rc
实现引用计数,RefCell
提供运行时的可变性检查,用于实现更复杂的数据结构。
11. 并发编程
- Rust 的所有权机制让多线程编程更加安全。
- 使用
std::thread
库可以方便地创建线程,并且借助Arc
和Mutex
等类型来实现线程间的数据共享和同步。
12. 生命周期(Lifetimes)
- Rust 使用生命周期注解来管理引用的生命周期,确保程序不会引用无效数据。
- 生命周期的概念是 Rust 内存安全的重要组成部分,编译器会自动推断大部分生命周期,但有时需要显式标注。
OS 实现
第二阶段主要分为八个章节,每个章节层层递进,深入揭示操作系统的底层逻辑以及实现原理。
lab1
为了实现 sys_task_info 系统调用,首先在 TaskManager 中为任务控制块 (TCB) 扩展结构体,加入如下字段:sys_call_times:[u32;MAX_SYSCALL_NUM],然后在在mod.rs中增加increase_sys_call和get_sys_call_times函数,进而在syscall函数中调用increase_sys_call函数,
在系统调用处理逻辑中,维护当前任务的系统调用次数计数,每次进入系统调用时在数组中相应位置加一。在 sys_task_info 系统调用实现中,将当前任务的状态(应为 Running)、系统调用次数、以及通过 get_time() 获取的任务总运行时间写入 TaskInfo 结构体。
lab2
- 完成sys_get_time和sys_task_info函数,需要定义一个translated_struct_ptr,它通过页表(PageTable)将一个指向结构体(
*mut T
)的指针翻译为对应的物理地址,并返回一个可变引用(&’static mut T),这样可以直接操作映射后的物理内存。在sys_task_info函数中,需要获取系统调用时间和任务运行时间,所以需要在task.rs中定义并实现它们。 - 完成sys_mmap和sys_munmap函数,用于申请和释放虚拟内存映射。sys_mmap 通过指定的起始地址 start、长度 len 和内存页属性 port 来映射一段物理内存到虚拟地址空间。该函数会检查起始地址对齐情况、port 的合法性,并将内存页映射为可读、可写或可执行。sys_munmap 则用于取消内存映射,释放从 start 开始的一段虚拟内存。(注意,需要判断地址是否对齐)
lab3
- 实现了自定义的spawn的系统调用来创建新进程,以便简化进程创建的过程而无需使用fork+exec的组合操作。通过sys_spawn,直接从指定的路径启动目标程序,成功返回子程序的进程id,否则返回-1。
- 实现了stride调度算法,为每个进程设置优先级与其调度权重,使得系统资源能够更公平分配。
stride
调度通过设置初始优先级与动态步长(pass值),优先调度累计步长最小的进程,并对选中的进程步长进行累加调整,确保各进程的运行比例与优先级成正比。我还新增了sys_set_priority
系统调用,以便动态调整进程优先级,增强调度灵活性。
lab4
在本次作业中,我实现了三个系统调用:linkat、unlinkat 和 fstat,以支持硬链接和文件状态获取。
- linkat:该调用用于创建一个文件的硬链接。它接收原有文件路径和新链接路径作为参数,并将新路径指向与原文件相同的磁盘块。在实现中,我确保在创建链接时不允许同名文件的存在,避免潜在的未定义行为。
- unlinkat:此调用用于删除文件的链接。当调用 unlinkat 时,如果文件的引用计数降至零,它将回收与文件关联的 inode 及其数据块。我的实现确保正确处理文件的彻底删除,维护文件系统的一致性。
- fstat:该调用获取指定文件描述符的状态信息。它将文件状态结构体填充以提供有关文件的详细信息,如大小、权限和时间戳等,便于用户或其他系统调用进行进一步处理。
通过这三个系统调用的实现,我的文件系统支持了硬链接的创建与删除,以及文件状态的查询,从而增强了其功能和灵活性。
lab5
我实现的功能包括对进程和线程的资源管理,主要是通过互斥量(mutex)和信号量(semaphore)来控制并发访问。具体而言,为每个线程维护了四个关键数据结构:m_allocation 和 s_allocation 用于记录已分配的互斥量和信号量的数量,m_need 和 s_need 则用于跟踪线程尚需的资源数量。在系统调用中,加入了对死锁的检测逻辑,确保在尝试获取新资源之前检查当前资源的可用性。如果发现潜在的死锁情况,则会拒绝资源请求,并返回相应的错误代码。此外,还实现了调整资源需求的方法,以便在资源分配和释放时动态更新状态。