总结
去年就已经参加一次了,但是止步第一阶段,今年能做完第二阶段已经出乎我的意料了,收获良多。
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 | // easy-fs/src/layout.rs |
在从目录中读取目录项,或将目录项写入目录时,需要将目录项转化为缓冲区(即字节切片)的形式来符合 read_at OR write_at 接口的要求
1 | // easy-fs/src/layout.rs |