0%

2024秋操作系统训练营第二阶段总结——707state

总结

去年就已经参加一次了,但是止步第一阶段,今年能做完第二阶段已经出乎我的意料了,收获良多。

syscall

syscall是怎么发起的,系统调用时发生了哪些事情,这些都是第一次接触。

收获

spawn实现

如果不考虑其他因素,其实spawn类似于vfork+exec。

假定fork采用的是直接复制的策略,那vfork就是采用的阻塞父进程执行、获得父进程地址空间的引用、子进程运行、执行完成、恢复父进程。

fork 调用

主要部分是调用TaskControlBlock::fork, 返回父进程的TaskControlBlock的拷贝,并分配一个新的Pid。

Task部分各个结构的关系

TaskControlBlock

TaskControlBlock 代表一个任务或进程的控制块,用于存储任务的基本信息和状态。它包含了所有不会在运行时改变的内容,如进程ID (pid) 和内核栈 (kernel_stack)。此外,它还包含一个可变的 inner 字段,该字段封装了实际的任务状态信息:

pid:任务的唯一标识符。
kernel_stack:内核栈,用于保存该任务在内核态运行的栈信息。
inner:包含该任务的动态状态信息,用于存储在运行中可能变化的内容,如内存空间、任务上下文、进程树信息等。

TaskControlBlockInner

TaskControlBlockInner 是 TaskControlBlock 的内部状态结构体,用于存储运行期间动态变化的内容,如任务的上下文、内存管理、父子关系等。每个 TaskControlBlockInner 都包含以下字段:

trap_cx_ppn:存储陷入上下文(Trap Context)的物理页号,用于保存用户态的CPU上下文。
base_size:应用程序的基本大小,用于约束任务在内存中的地址空间。
task_cx:任务上下文,表示当前任务的 CPU 状态。
task_status:当前任务的状态(如 Ready、Running、Zombie)。
memory_set:用于管理该任务的地址空间。
parent 和 children:当前任务的父子进程关系。
exit_code:任务退出时的状态码。
heap_bottom 和 program_brk:用于管理堆内存的范围。

TaskManager

负责调度所有准备好运行的Task,它维护了一个 ready_queue 队列,包含了所有准备好运行的任务的 TaskControlBlock,从中取出任务并将其交给调度器:

ready_queue:一个队列,存储处于“Ready”状态的任务。

add 和 fetch:add 将任务添加到 ready_queue 中,fetch 从队列中取出任务进行调度。

文件系统

目录项

目录项

对于文件而言,它的内容在文件系统或内核看来没有任何既定的格式,只是一个字节序列。目录的内容却需要遵从一种特殊的格式,它可以看成一个目录项的序列,每个目录项都是一个二元组,包括目录下文件的文件名和索引节点编号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// easy-fs/src/layout.rs

const NAME_LENGTH_LIMIT: usize = 27;

#[repr(C)]
pub struct DirEntry {
name: [u8; NAME_LENGTH_LIMIT + 1],
inode_number: u32,
}

pub const DIRENT_SZ: usize = 32;

impl DirEntry {
pub fn empty() -> Self;
pub fn new(name: &str, inode_number: u32) -> Self;
pub fn name(&self) -> &str;
pub fn inode_number(&self) -> u32
}

在从目录中读取目录项,或将目录项写入目录时,需要将目录项转化为缓冲区(即字节切片)的形式来符合 read_at OR write_at 接口的要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// easy-fs/src/layout.rs

impl DirEntry {
pub fn as_bytes(&self) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
self as *const _ as usize as *const u8,
DIRENT_SZ,
)
}
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
unsafe {
core::slice::from_raw_parts_mut(
self as *mut _ as usize as *mut u8,
DIRENT_SZ,
)
}
}
}