第一阶段
工作中已经写了一年多的Rust代码,体验确实不错,特别是完善的包管理工具,大大减少了编译时的心智消耗,所有权和借用系统并没有很复杂,写一阵代码自然就熟悉了
第一阶段算是再巩固下基础,数据结构题比较有意思,不过深入学习还是要抽空看看链表
第二阶段
对操作系统很感兴趣,尤其是第三阶段的虚拟化方向。由于平时工作比较忙,也就晚上和周末抽时间抓紧写写代码,下边主要总结下各个章节产生的疑问和实验遇到的问题吧
lab1(ch2,ch3)
主要熟悉了应用加载,中断,用户栈/内核栈切换,任务切换的机制,再是学习了下risc-v
寄存器以及汇编方面的知识,受益匪浅
实验比较简单,主要熟悉下syscall
的开发流程
lab2(ch4)
本章新增了地址空间,内核和应用通过页表机制把虚拟内存映射到实际的物理内存,虚拟内存使得不同应用都可以使用连续独立的地址空间,并做到了不同应用之间的隔离
strampoline
段用于保存陷入内核和返回用户态的代码,并通过把内核和应用的strampoline
设置成相同地址的虚拟页(最高页),使得切换地址空间之后,这段代码地址可以无缝衔接
TrapContext
紧贴着strampoline
位于应用的次高页(看起来没必要一定要在次高页)。TrapContext
用于保存上下文切换时的寄存器以及kernel satp
和kernel sp
等。TrapContext
位于用于应用地址空间是因为,切换上下文时只有一个sscratch
寄存器可以用于周转,而如果TrapContext
位于内核栈(内核栈不是恒等映射),那就只需要先通过sscratch
得到kernel satp
切换到内核地址空间之后才能访问TrapContext
,这时就没有额外寄存器获得kernel sp
也就拿不到TrapContext
地址。如果位于应用地址空间,就可以通过sscratch
寄存器保存TrapContext
地址,在陷入内核后,先在应用地址空间保存好上下文之后,再切换到内核地址空间
实验需要注意的点:
mmap页面要4k对齐,空间左闭右开
syscall复制结构体时,需要通过
translated_byte_buffer
拿到用户态结构体对应的物理地址(这部分内存在内核是恒等映射的,可以正确写入),再复制
lab3(ch5,ch7)
这两章主要讲了进程和进程通信,是概念相对简单的章节,难点主要在于调度算法
实验是实现spawn,总体来说是new+fork,不复制地址空间,但继承父子关系
lab4(ch6)
本章学习了文件系统,代码中数据结构依赖关系相对复杂,画图梳理下比较方便理解
实验中nlink
需要要保存在DiskInode
中,同时减少INODE_DIRECT_COUNT
保证DiskInode
只占用128bytes
这里我把file_count
也存下了,方便unlink
时候,直接把最后一个DirEntry
替换到被删除的Entry
位置。其次可以用union
保存nlink
和file_count
来节省空间,毕竟文件没有file_count
,目录不允许有link
,这里偷懒了
1 | const INODE_DIRECT_COUNT: usize = 26; |
一定要注意锁不能重入,在开发unlinkat
时,因为调用其他也加文件锁的函数导致了死锁,被卡了有一阵
lab5(ch8)
本章主要讲了内核中线程和锁的实现机制
下面有几个注意点
调用
schedule
切换线程前一定要手动drop
资源,否则会造成资源泄露或panic
exit_current_and_run_next
在调用schedule
之前分配了TaskContext::zero_init()
作为dummy TaskContext,开始还想着这个线程不会再恢复了,那这个栈空间在后边是如何保证地址合法并最终回收的,而这就是sys_waittid
存在价值之一代码中的
PROCESSOR
并不是thread_local
的,但有很多exclusive_access
的调用,可能教学用的rCore并不存在多个cpu,这里暂时不需要考虑这个问题
1 | lazy_static! { |
实验是死算检测,并不复杂,按着文档来就好