Skip to content

Commit e5c8cd3

Browse files
committed
update
1 parent fc846fd commit e5c8cd3

File tree

8 files changed

+480
-1
lines changed

8 files changed

+480
-1
lines changed

os/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ KERNEL_ENTRY_PA := 0x80200000
2222
OBJDUMP := rust-objdump --arch-name=riscv64
2323
OBJCOPY := rust-objcopy --binary-architecture=riscv64
2424

25-
CHAPTER ?= $(shell git rev-parse --abbrev-ref HEAD | sed -E 's/ch([0-9])/\1/')
25+
CHAPTER ?= $(shell git rev-parse --abbrev-ref HEAD | sed -E 's/ch()/\1/')
2626
TEST ?= $(CHAPTER)
2727
BASE ?= 1
2828

os/src/trap/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,17 @@ pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext {
4141
let scause = scause::read(); // get trap cause
4242
let stval = stval::read(); // get extra value
4343
match scause.cause() {
44+
// 处理系统调用 (UserEnvCall)
4445
Trap::Exception(Exception::UserEnvCall) => {
4546
cx.sepc += 4;
4647
cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
4748
}
49+
// 处理访存错误 (StoreFault / StorePageFault)
4850
Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) => {
4951
println!("[kernel] PageFault in application, kernel killed it.");
5052
run_next_app();
5153
}
54+
// 处理非法指令 (IllegalInstruction)
5255
Trap::Exception(Exception::IllegalInstruction) => {
5356
println!("[kernel] IllegalInstruction in application, kernel killed it.");
5457
run_next_app();

reports/Lab 3.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# `Lab 3`
2+
3+
## 荣誉准则
4+
5+
1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 **以下各位** 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容:
6+
7+
>
8+
9+
2. 此外,我也参考了 **以下资料** ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容:
10+
11+
>
12+
13+
3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。
14+
15+
4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。
16+
17+
------
18+
19+
## 问答题解答
20+
21+
### 问题1:实际情况是轮到 p1 执行吗?为什么?
22+
23+
不是。实际情况是 p2 会再次执行。
24+
25+
**原因**
26+
- p1.stride = 255,p2.stride = 250
27+
- p2 执行一个时间片后,stride 增加 pass=10,得到 250+10=260
28+
- 由于使用 8bit 无符号整数,260 会溢出变成 4(260 mod 256 = 4)
29+
- 此时 p1.stride = 255,p2.stride = 4
30+
- 由于 4 < 255,所以 p2 会再次被执行,而不是 p1
31+
32+
### 问题2:为什么 STRIDE_MAX – STRIDE_MIN <= BigStride / 2?
33+
34+
**原因**
35+
- 进程优先级 ≥ 2,所以每个进程的 pass = BigStride / priority ≤ BigStride / 2
36+
- 当某个进程的 stride 落后其他进程超过 BigStride / 2 时,它会被立即调度
37+
- 因为它的 stride 最小,会被调度器选中
38+
- 调度后它的 stride 增加 pass ≤ BigStride / 2,仍然不会超过其他进程的 stride 超过 BigStride / 2
39+
- 因此,所有进程的 stride 差值始终不会超过 BigStride / 2
40+
41+
### 问题3:补全比较器代码
42+
43+
```rust
44+
use core::cmp::Ordering;
45+
46+
const BIG_STRIDE: u64 = 255; // 假设 BigStride 为 255
47+
48+
struct Stride(u64);
49+
50+
impl PartialOrd for Stride {
51+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
52+
let a = self.0;
53+
let b = other.0;
54+
let diff = (a as i16).wrapping_sub(b as i16);
55+
56+
if diff > 0 {
57+
// a > b
58+
if diff <= (BIG_STRIDE / 2) as i16 {
59+
Some(Ordering::Greater)
60+
} else {
61+
// 差值过大,说明 b 溢出了,实际 b > a
62+
Some(Ordering::Less)
63+
}
64+
} else {
65+
// a < b
66+
if (-diff) <= (BIG_STRIDE / 2) as i16 {
67+
Some(Ordering::Less)
68+
} else {
69+
// 差值过大,说明 a 溢出了,实际 a > b
70+
Some(Ordering::Greater)
71+
}
72+
}
73+
}
74+
}
75+
76+
impl PartialEq for Stride {
77+
fn eq(&self, other: &Self) -> bool {
78+
false
79+
}
80+
}
81+
```
82+
83+
**原理**
84+
- 利用有符号整数的环绕减法计算差值
85+
- 如果差值的绝对值 ≤ BigStride / 2,说明没有跨越溢出边界,直接比较
86+
- 如果差值的绝对值 > BigStride / 2,说明跨越了溢出边界,实际大小关系与表面相反
87+
88+
## 功能总结
89+
90+
我实现了两个系统调用:
91+
92+
1. **`sys_set_priority(prio: isize) -> isize`**
93+
- 设置当前进程的优先级
94+
- 优先级必须 ≥ 2,否则返回 -1
95+
- 设置成功后重新计算步长 pass = BIG_STRIDE / priority
96+
- 返回设置的优先级
97+
98+
2. **`sys_spawn(path: *const u8) -> isize`**
99+
- 创建新的子进程执行指定程序
100+
- 不复制父进程地址空间,直接加载目标程序
101+
- 建立正确的父子进程关系
102+
- 设置子进程返回值为 0
103+
- 成功返回子进程 PID,失败返回 -1
104+
105+
同时实现了 stride 调度算法,为每个进程维护 stride、pass、priority 字段,调度时选择 stride 最小的进程,确保高优先级进程获得更多 CPU 时间。

reports/Lab 4.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Lab 4
2+
3+
## 荣誉准则
4+
5+
1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 **以下各位** 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容:
6+
7+
>
8+
9+
2. 此外,我也参考了 **以下资料** ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容:
10+
11+
>
12+
13+
3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。
14+
15+
4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。
16+
17+
------
18+
19+
## 问答题解答
20+
21+
## ch6:
22+
23+
### 1. 在我们的easy-fs中,root inode起着什么作用?如果root inode中的内容损坏了,会发生什么?
24+
25+
答:
26+
27+
Root Inode 在 Easy-FS 中的作用:
28+
29+
1. **文件系统的入口点**
30+
31+
- **根目录**:Root inode 代表文件系统的根目录 `/`
32+
- **起点**:所有文件和目录的查找都从 root inode 开始
33+
- **目录结构**:包含文件系统的初始目录项,指向其他文件和子目录
34+
35+
2. **持久化存储**
36+
37+
- 在 Easy-FS 中,root inode 通过静态变量持久化
38+
39+
如果 Root Inode 损坏的后果
40+
41+
1. **灾难性的数据丢失**
42+
43+
- **完全无法访问**:整个文件系统变得不可访问
44+
- **连锁反应**:由于所有路径解析都从 root inode 开始,损坏会导致所有操作失败
45+
46+
2. **具体故障表现**(如:元数据损坏、目录项损坏)
47+
48+
### 2.介绍我完成的代码
49+
50+
我实现了三个文件系统相关的系统调用:
51+
52+
**fstat**:获取文件状态信息。通过文件描述符找到对应的文件,返回文件的元数据,包括设备ID、inode号、文件类型和硬链接计数等。
53+
54+
**linkat**:创建硬链接。接受原文件名和新文件名作为参数,在文件系统中创建指向同一inode的硬链接,同时增加原文件的链接计数。
55+
56+
**unlinkat**:删除文件链接。删除指定路径的文件目录项,减少文件的硬链接计数。当链接数降为0时,回收文件占用的数据块资源。
57+
58+
这些实现基于Easy-FS文件系统,支持基本的文件链接管理和状态查询功能,为应用程序提供了POSIX兼容的文件操作接口。
59+
60+
## ch7:
61+
62+
### 1. 使用 Pipe 的实际应用例子
63+
64+
在 Linux terminal 中,我们经常使用管道将多个命令连接起来。例如统计文件行数:
65+
66+
```bash
67+
cat file.txt | wc -l
68+
```
69+
70+
这里:
71+
- `cat file.txt` 输出文件内容到标准输出
72+
- 管道 `|``cat` 的输出重定向为 `wc -l` 的输入
73+
- `wc -l` 统计输入的行数并输出结果
74+
75+
### 2. 如果需要在多个进程间互相通信,则需要为每一对进程建立一个管道,非常繁琐,请设计一个更易用的多进程通信机制。
76+
77+
**设计:基于命名的消息队列系统**
78+
79+
1. **命名消息队列**
80+
- 每个队列有唯一名称,进程通过名称访问
81+
- 支持多个生产者和消费者
82+
83+
2. **发布-订阅模式**
84+
- 进程可以订阅感兴趣的消息类型
85+
- 消息按主题分类,自动路由到订阅者
86+
87+
3. **共享内存区域**
88+
- 建立共享内存段用于高效数据传输
89+
- 配合信号量进行同步控制
90+
91+
4. **中央消息路由器**
92+
- 一个专门的消息路由进程
93+
- 负责消息的接收、分类和分发
94+
- 提供消息持久化和确认机制
95+
96+
这种设计比点对点管道更灵活,支持一对多、多对多通信模式,大大简化了多进程间的通信复杂度。

reports/Lab 5.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# `Lab 5
2+
3+
## 荣誉准则
4+
5+
1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 **以下各位** 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容:
6+
7+
>
8+
9+
2. 此外,我也参考了 **以下资料** ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容:
10+
11+
>
12+
13+
3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。
14+
15+
4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。
16+
17+
------
18+
19+
## 问答题解答
20+
21+
## 1. 进程退出时的资源回收
22+
23+
**需要回收的资源包括:**
24+
25+
1. **线程控制块 (TaskControlBlock)**:所有子线程的TCB
26+
2. **内核栈**:每个线程的内核栈空间
27+
3. **用户栈**:每个线程的用户栈空间
28+
4. **虚拟内存资源**:进程的地址空间、页表等
29+
5. **文件描述符**:进程打开的文件资源
30+
6. **同步原语**:进程创建的mutex、semaphore等
31+
7. **死锁检测状态**:进程的死锁检测器相关数据结构
32+
33+
**TaskControlBlock可能被引用的位置及回收情况:**
34+
35+
1. **进程的线程列表中**:必须回收,因为这是进程直接管理的资源
36+
2. **就绪队列中**:必须回收,否则会导致调度器访问无效的TCB
37+
3. **等待队列中**(mutex、semaphore等):必须回收,否则唤醒时会访问无效TCB
38+
4. **条件变量等待队列中**:必须回收
39+
5. **父子进程关系中**:必须回收,避免孤儿进程
40+
6. **定时器队列中**:必须回收,避免定时器触发时访问无效TCB
41+
42+
**回收原因**:这些引用如果不及时回收,会导致use-after-free、内存泄漏、系统状态不一致等问题。
43+
44+
## 2. 两种Mutex实现的区别及问题
45+
46+
**主要区别:**
47+
48+
1. **锁获取逻辑不同**
49+
- Mutex1:使用循环重试,确保被唤醒后重新检查锁状态
50+
- Mutex2:无循环,被唤醒后直接认为获得锁
51+
52+
2. **锁释放逻辑不同**
53+
- Mutex1:总是将locked置为false,然后唤醒等待线程
54+
- Mutex2:只有在等待队列为空时才将locked置为false
55+
56+
**可能导致的问题:**
57+
58+
1. **虚假唤醒问题**:Mutex2在被唤醒后没有重新检查锁状态,如果存在虚假唤醒或多个线程同时被唤醒,可能导致多个线程同时进入临界区,破坏互斥性。
59+
60+
2. **锁状态不一致**:Mutex2在唤醒等待线程时没有释放锁标记,被唤醒的线程看到的锁状态仍然是locked=true,但实际上锁应该已经释放。
61+
62+
3. **公平性问题**:Mutex1通过循环确保先到先服务,Mutex2可能产生饥饿现象。
63+
64+
4. **死锁风险**:在Mutex2中,如果唤醒的线程因为某种原因没有运行,锁状态可能一直保持locked=true,导致其他线程无法获取锁。
65+
66+
## 3. 功能实现总结
67+
68+
我实现了一个基于银行家算法的死锁检测机制。该机制将mutex和semaphore抽象为资源类,维护Available、Allocation、Need三个核心矩阵。在资源申请时执行安全状态检查,如果检测到潜在死锁则拒绝请求并返回特定错误码。通过新的系统调用允许进程按需启用/禁用死锁检测功能,保持向后兼容性。实现涉及死锁检测器数据结构、系统调用包装、资源状态管理等模块。
69+
70+
完成本次实验大约用了6小时,主要时间花费在理解银行家算法原理、设计数据结构集成方案、调试线程ID获取和资源状态同步等问题上。

0 commit comments

Comments
 (0)