mirror of
https://github.com/rcore-os/rCore.git
synced 2024-11-25 01:16:18 +04:00
324 lines
17 KiB
Markdown
324 lines
17 KiB
Markdown
## 日志
|
||
|
||
这里以天为单位,以周为周期,按时间倒序维护了开发日志。
|
||
|
||
### 第15周
|
||
|
||
#### 2018.06.06:FS-Process
|
||
|
||
- 学习xv6/uCore中文件系统上层接口
|
||
- 尝试在Rust中实现RootFS和FileManager
|
||
|
||
#### 2018.06.04:完成同步互斥
|
||
|
||
- 完成了信号量、条件变量,哲学家就餐问题
|
||
- 简易实现了std::sync::mpsc中的FIFO消息传递通道
|
||
|
||
#### 2018.06.03:新的物理帧分配器
|
||
|
||
- 实现了新的物理帧分配器,支持回收。其本质是一个0-2^k的整数分配器,内部用bitset维护,并形成树状级联结构,每16bit对应上层1bit,用x86专有指令bsr(相当于整数log2)实现快速分配。目前使用容量为64Kb的分配器,可维护256MB内存空间,实际占用内存9KB以内。
|
||
- 由于Rust语言的限制,该类无法实现const fn构造函数,只能使用lazy_static等tradeoff进行运行时初始化。这时必须开启至少O1优化(使用RVO返回值优化,防止在栈上构造再复制到全局变量),否则会栈溢出。
|
||
|
||
#### 2018.06.02:重构内存管理模块
|
||
|
||
- 大幅调整了内存管理模块,对外统一使用MemorySet管理一个线程的内存,简化了使用方式。
|
||
|
||
#### 2018.06.01:同步互斥进阶
|
||
|
||
- 完成了OS内用锁实现的哲学家就餐问题,和OS外用条件变量的实现。
|
||
- 关于Rust并发模型:Rust的所有权机制使得它可以轻松支持【锁数据】而不是【锁代码】。这使得在实现上述哲学家就餐问题时,用锁非常自然,而用条件变量(管程)则不太直观。
|
||
|
||
### 第14周
|
||
|
||
#### 2018.05.31:同步互斥初步
|
||
|
||
* 将spin::Mutex代码fork了一份到内核中,在其中实现了一个接口框架,可以自由替换底层支持。在此框架下,已经实现了自旋锁(spin_lock)、禁止中断的自旋锁(spin_lock_irqsave),接下来就可以实现自动调度的锁了。
|
||
* 实现了禁止中断自旋锁后,即可打开syscall的中断了。
|
||
* 为了方便内核态的线程操作,将原有的process模块又封装了一层thread接口,长得和标准库一模一样(std::thread)。这样具体的同步互斥问题(如哲学家就餐)可以先在外部环境中依赖std库实现并测试,然后方便地移植到OS环境中。
|
||
* 在此过程中又学习了关于内核抢占、同步互斥锁等相关资料,感觉又是个大坑。并且发现我实现的RustOS一直是内核可抢占的,主要依赖时钟中断进行线程切换,不知要不要改掉。
|
||
|
||
### 第13周
|
||
|
||
本周完成的主要工作:
|
||
|
||
- 重新实现中断处理入口
|
||
- 实现内核态switch和调度器
|
||
- 从disk0中读取用户程序
|
||
- 支持运行大部分ucore用户程序
|
||
|
||
#### 2018.05.24 / 25:最终报告
|
||
|
||
#### 2018.05.23:实现了调度器,IDE驱动
|
||
|
||
- 参考uCore,实现了RRScheduler和StrideScheduler,并可以正确执行priority程序。
|
||
- 直接Copy了隔壁15组port的ucore的IDE驱动代码,略作修改后就可以使用了。
|
||
- 为SFS模块增加了一个BlockedDevice接口,为它实现Device接口(即支持以字节为单位读写),方便对接真实设备。
|
||
|
||
#### 2018.05.22:实现了xv6/ucore中的switch(),调整了切换进程的方式
|
||
|
||
- 为了实现调度器,我阅读了xv6的文档,结果发现自己之前对它切换进程方式的理解都是错的![捂脸] (长教训,以后一定硬着头皮读懂已有代码,不能闭门造车233)
|
||
- 之前:每次中断后运行的内核态代码和内核栈是不可切换的,切换进程的方法是在结束中断处理时,指定新的rsp地址,这样就可以从别的中断帧恢复环境。这样带来的限制是像sys_wait这种异步操作实现起来很麻烦。
|
||
- 有了switch()之后,就可以自由地在内核线程之间切换。我今天实现了这个,并用它简化了sys_wait。
|
||
- 引入switch()后,要运行一个新的线程变得更加复杂,初始的内核栈上要有TrapFrame+Context。此外我发现调用switch进入scheduler线程,再switch到其它线程,这一过程好像用中断也可以实现(类似上述第2条描述的方法)。
|
||
|
||
#### 2018.05.21:可以正常跑uCore大部分的用户程序
|
||
|
||
- 实现了在用户程序发生异常时kill掉它,又支持了一批uCore的用户程序。有了昨天统一中断处理的基础,这个就很容易了。
|
||
|
||
- 使用mksfs将xv6的64位用户程序也做成了SFS磁盘,将它和ucore程序的SFS磁盘一起添加到git中。
|
||
|
||
- 修复了Release模式下SMP启动的Bug,添加了一些volatile。
|
||
|
||
- 修复了waitpid,可以正常跑exit和sleep了。
|
||
|
||
注:此时还不支持内核线程的切换(详见05.22的说明),所以(int*)store要先保存下来,不太自然。
|
||
|
||
#### 2018.05.20:可以正常跑uCore一半的用户程序
|
||
|
||
写了一个简易事件处理器,支持定期调度+唤醒线程
|
||
|
||
- 最初实现的是【推模式】,逻辑比较复杂,用户提供回调函数,当事件发生时自动调用。由于Rust的安全约束非常严格,为了通过编译,代码非常复杂。
|
||
- 最后实现的是【拉模式】,逻辑比较简单,用户只提供事件内容,在每个时刻都主动询问当前是否有事件,并做处理。这种方式下事件处理器很轻巧,可以内嵌在使用者中,调用关系简洁。
|
||
|
||
重写了中断处理入口
|
||
|
||
- 原来使用Redox的处理方式:
|
||
- 每个中断有不同的入口,保存的中断帧可以有不同形式(性能考虑?)。
|
||
- 但现在需要每种中断都能够进行进程切换,这就需要一个统一的中断帧和处理过程。
|
||
- 现在改用ucore/xv6的处理方式:
|
||
- 建立中断向量表vectors,补齐错误码,跳转到统一的处理函数alltraps。
|
||
- ucore/xv6中vectors.S是用一个perl脚本生成的,在Rust中改用build.rs生成。
|
||
- 如此修改后,逻辑更加简洁清晰,而且修复了一个长期阴魂不散的Bug
|
||
|
||
精简了IDT的代码
|
||
|
||
- 最初使用x86_64库的IDT结构;之后由于类型不匹配的原因,改用Redox的代码
|
||
- 现在随着我姿势水平的提高,学会了绕过类型约束的unsafe黑魔法,重新用起了x86_64库,砍掉了100行的基础代码
|
||
- 历史总是螺旋式上升的,马克思诚不我欺也
|
||
|
||
Travis上编译问题
|
||
|
||
- 目前在macOS和docker上均可通过编译
|
||
- 但在Travis上会出现链接问题,不知如何解决
|
||
|
||
#### 2018.05.19:引入log模块,支持向终端输出彩色文字
|
||
|
||
- 可以用五种不同级别输出调试信息:error,warn,info,debug,trace
|
||
- 它们在终端以不同颜色显示,使用了Linux终端控制符
|
||
|
||
#### 2018.05.18:可以从sfs.img中加载用户程序
|
||
|
||
- 将sfs.img做成.o硬链接到kernel,配合之前写好的SFS模块读取数据
|
||
|
||
### 第12周
|
||
|
||
本周完成的主要工作:
|
||
|
||
- 可以运行用户程序(xv6的64位程序 + ucore的32位程序)
|
||
|
||
#### 2018.05.18:可以在CLion中配合gdb调试
|
||
|
||
- x86_64的QEMU在gdb链接时会报错:Remote 'g' packet reply is too long
|
||
- 其实《Writing an OS in Rust》已经给出了[解决方案](https://os.phil-opp.com/set-up-gdb/)。然而我build gdb时编译出错。。
|
||
- 最后解决方案:(macOS下)使用brew安装 altkatz/gcc_cross_compilers/x64-elf-gcc。这个自带上述bug的补丁,在CLion中配置使用这个gdb即可。
|
||
|
||
#### 2018.05.17:可以运行ucore的32位用户程序
|
||
|
||
- 不能直接在Long Mode下跑,因为有些指令是环境相关的,例如push 0x64会使得esp -= 8
|
||
- 因此需要进入Compatibility Mode,这方面资料好像很少。[OSDev的一个贴子](https://forum.osdev.org/viewtopic.php?f=1&t=24594)指出只需载入32位的CS即可,即修改L-Bit,但经测试无效,OS会将其识别为16位CS。之后我把xv6的UCODE和UDATA 32位段描述符直接复制过来,就可以正常跑了。经测试,DS也需要是32位的。
|
||
- 目前已经实现了若干ucore系统调用,可以跑hello程序了
|
||
|
||
#### 2018.05.14:Shared-memory & Copy-on-write
|
||
|
||
- 利用Rust的Trait特性,将扩展代码都写在一个文件中:[代码](https://github.com/wangrunji0408/RustOS/blob/dev/src/arch/x86_64/paging/cow.rs)。同时包含文档和测试。
|
||
|
||
#### 2018.05.13:可以运行用户态程序
|
||
|
||
- 可直接运行xv6 x86_64中的用户程序(二进制兼容),下一步兼容ucore的32位用户程序
|
||
- 实现了一个最简单的syscall:write,可将字符输出到屏幕上
|
||
- 试图实现fork时遇到bug…
|
||
|
||
#### 2018.05.12:添加MemorySet / Area结构
|
||
|
||
- 对应ucore中的mm&vma,用来描述虚拟内存集/段
|
||
- 将【内核页表重映射 remap_the_kernel】过程用此结构重构
|
||
- 为下一步加载用户态程序做准备
|
||
|
||
### 第11周
|
||
|
||
本周完成的主要工作:
|
||
|
||
- 通过兼容层将Rust SFS对接到ucore_os_lab上
|
||
|
||
#### 2018.05.07:完成与ucore_os_lab的对接
|
||
|
||
- [总结报告](https://github.com/wangrunji0408/SimpleFileSystem-Rust/blob/master/docs/rust_port_report.md)(后半部分)
|
||
- [整合后的ucore](https://github.com/wangrunji0408/ucore_os_lab/tree/rust-fs/labcodes_answer/lab8_result)
|
||
|
||
#### 2018.05.06:把SFS链接到uCore
|
||
|
||
将Rust lib链接到ucore遇到的问题:
|
||
|
||
- 真的痛苦,估计得掉层皮
|
||
- 遇到最多的是链接丢失问题。ucore_os_lab的linker script少写了一些section,包括`*.data.*`,`*.got.*`,`*.bss.*`,导致Rust lib链接过去后丢失了一些段,比较坑的是这不会有任何提示。没有成功重定位的地址都是0,运行时直接Page fault。为了找出出错位置,各种gdb,objdump全上了,还得看汇编追踪寄存器,真是大坑。
|
||
- 另一个小问题是Rust lib会引用一些LLVM内置函数(如udivdi3,都是除法运算相关),链接时会报undefined symbol。但实际上并没有代码用到它们。《Writing an OS in Rust》中提到了这个问题,它的解决方案是链接时加--gc-sections选项,将未用到的段删掉,结果我对ucore如此操作之后所有段都没了,都boot不起来。。。最后是在C中强行定义这些符号解决的。
|
||
|
||
关于ucore VFS兼容层的设计:
|
||
|
||
- ucore VFS中fs和inode头部是具体FS的struct。Rust VFS使用Rc指针相互引用。为了将它们合并起来,考虑过两种方案:在头部放Rc指针,或放Rust SFS的结构本体。前者相当于将Rust VFS作为ucore SFS,后者则是直接将Rust VFS合并到ucore VFS。
|
||
- 放指针:还需建立Rust INode => ucore INode的反向引用,要么侵入式地增加Rust INode的字段,要么在兼容层搞一个全局Map。这种方式耦合较低,但多一层指针跳转,性能可能略差。
|
||
- 放结构:需要在Rust new出SFS结构时做文章, 委托ucore分配多一点空间并做VFS的初始化,得魔改Rust的全局内存分配器。这种方式耦合较高,需对Rust结构的实际内存布局有深入理解。
|
||
- 根据【把方便留给别人,把困难留给自己】【最大兼容,最小耦合】的原则,我选了放指针的方案,目前还在Debug中`_(:3」∠)_`
|
||
|
||
### 第10周
|
||
|
||
本周完成的主要工作:
|
||
|
||
- 进程模块:只实现了内核线程切换
|
||
- 文件系统:作为独立模块实现完毕,接下来尝试链接到ucore lab8
|
||
|
||
#### 2018.05.04 / 05:SFS文件系统
|
||
|
||
- 基本功能实现完毕,附有单元测试
|
||
- 正在尝试和ucore lab8链接
|
||
- 阶段性[移植报告](https://github.com/wangrunji0408/SimpleFileSystem-Rust/blob/master/docs/rust_port_report.md)
|
||
|
||
#### 2018.04.29 / 05.01:SFS文件系统
|
||
|
||
[GitHub仓库](https://github.com/wangrunji0408/SimpleFileSystem-Rust)
|
||
|
||
基础结构移植完毕,各项功能正在重写,计划导出C接口兼容ucore,预计1k行代码可搞定
|
||
|
||
#### 2018.04.26 / 27:内核线程切换
|
||
|
||
- 实现了简单的内核线程切换
|
||
|
||
每个线程拥有一个[内核栈],它是当线程运行中发生中断时内核使用的栈,保存着线程的[上下文]信息(中断帧)。对于内核线程而言,其[运行栈]和[内核栈]是统一的。
|
||
|
||
与ucore不同的是:
|
||
|
||
- 每个线程的内核栈上只保存自己的[上下文],调度器不能修改它的内容。且[上下文]只需保存这一份,调度器也不需要访问它的内容。
|
||
- 恢复[上下文]不使用汇编,而是依靠[中断服务例程]结束时从[内核栈]中pop[中断帧]。
|
||
|
||
为了实现这点,[中断服务例程]保存完[中断帧]后,把当前rsp传给调度器,调度器将其保存,并用下一个待执行线程的rsp更新之(在最后一次从此线程切出时保存)。[中断服务例程]结束时,重置rsp,切换到另一个线程的[内核栈]上,恢复[中断帧]。
|
||
|
||
- 实现了int触发内核态和用户态切换(lab1 challenge)
|
||
|
||
进入用户态前,需在TSS中设置返回内核态时的rsp。FIXME:初始化多核后,会导致设置失效。
|
||
|
||
确认以下设置,否则会出现GPF:
|
||
|
||
- 段描述符DPL=3
|
||
|
||
- 页表中设置相应页为[用户可访问]
|
||
|
||
### 第9周
|
||
|
||
本周应付期中考试、大作业等事务,进展不大。
|
||
|
||
#### 2018.04.25:页置换算法
|
||
|
||
实现“改进的时钟置换算法”
|
||
|
||
### 第8周
|
||
|
||
本周完成的主要工作:
|
||
|
||
- 将Kernel虚地址移到高区
|
||
- 完成lab1的移植,主要是各种驱动
|
||
- 完成多核的初始化
|
||
|
||
接下来可以进行的工作:
|
||
|
||
- 完善内存管理:完善allocator,mm&vma结构,页替换算法
|
||
- 其它驱动的移植,例如IDE(Rucore组)
|
||
- 进程管理模块
|
||
|
||
#### 2018.04.19:内存模块
|
||
|
||
在外部模块定义了页替换模块接口,实现了FIFO算法,并实现了Mock页表用来单元测试。
|
||
|
||
#### 2018.04.18:多核启动
|
||
|
||
完成多核AP的启动和初始化:LocalAPIC,GDT,IDT
|
||
|
||
#### 2018.04.17:设备中断
|
||
|
||
完成以下设备初始化和中断:串口,键盘,PIT时钟
|
||
|
||
#### 2018.04.15: 高地址
|
||
|
||
完成高地址修改,合并到主分支
|
||
|
||
- (2天前)页表重映射后崩溃的原因,是新页表中权限设置错误(blog_os根据kernel.elf中各段的属性来设置页的权限),使用objdump查看elf的段信息后确认有问题,根本原因是linker script写的不对:我使用rodata32/bbs32/text32来标记BootLoader中的各段,以把它们和Kernel中的区别开,但这导致ld无法正确设置它们的属性,某些应该可写的段被设置为只读,最终造成PageFault。解决方案是把名称改成rodata.32/bss.32/text.32。
|
||
- [参考教程](https://wiki.osdev.org/Higher_Half_x86_Bare_Bones)
|
||
- 【新技能】在没有初始化中断时,出现TripleFault的Debug方法:利用qemu!
|
||
- 加入参数-d int,即可显示每次中断时的CPU信息
|
||
- 发生PageFault时,检查RPI(出错位置)和CR2(访存目标)
|
||
|
||
#### 2018.04.14:设备初始化
|
||
|
||
- 页表建立临时映射,以满足各设备初始化时访问特殊物理地址的需求。TODO:撤除or直接映射全部设备空间
|
||
- 完成以下初始化:LocalAPIC(链接C),IOAPIC,GDT(导入xv6 x86_64的描述符),PIC(复制Redox)
|
||
- PIC和APIC都实现了产生时钟中断
|
||
- [关于实现中断的问题手册](https://wiki.osdev.org/I_Cant_Get_Interrupts_Working)
|
||
|
||
#### 2018.04.13:高地址
|
||
|
||
尝试将Kernel虚地址移到高地址区,遇到很多问题
|
||
|
||
- 需要修改linker将kernel虚地址置为高地址区,但实地址还在低地址区(AT指令)
|
||
- 需要修改初始页表,将四级页表项的1st和510th同时映射到低1GB物理空间。由于BootLoader执行时PC使用实地址,因此在进入Rust之后才能把1st页表项撤销掉。
|
||
- 页表重映射(remap_the_kernel)修改后还没有调试成功,重置CR3的瞬间会崩掉
|
||
- 经过测试,初始化IDT只能在页表重映射后进行(放到前面会直接崩掉),在开启IDT前不好Debug。
|
||
|
||
### 第7周
|
||
|
||
#### 2018.04.12:中期汇报文档和PPT
|
||
|
||
- [中期汇报文档](https://github.com/wangrunji0408/RustOS/blob/dev/docs/MidReport.md)
|
||
- [中期汇报PPT](https://github.com/wangrunji0408/RustOS/blob/dev/docs/MidPresentation.pdf)
|
||
|
||
#### 2018.04.11:学习xv6,C语言绑定
|
||
|
||
- 阅读xv6代码
|
||
- 实现了Rust对C的绑定
|
||
- 直接extern符号链接即可,不需要bindgen
|
||
- bindgen输出的代码中,把std::raw::*替换成原生类型即可
|
||
- RISCV Rust Toolchain:找到了一个日本人写的[采坑系列文章](http://msyksphinz.hatenablog.com/entry/2017/11/29/021030),写于2017.12,成功在HiFive上跑起来了。
|
||
|
||
#### 2018.04.09:TravisCI,C语言绑定
|
||
|
||
- 从内部退出QEMU的方法:
|
||
- 运行qemu时加入 -device isa-debug-exit
|
||
- 执行outb(0x501, k),会退出qemu,错误码为2k+1
|
||
- [在Travis中运行qemu的方法](https://github.com/jdub/travis-qemu-example)
|
||
- 结合上述方法可以在Travis上做集成测试
|
||
- qemu环境下单测比较困难,rust自带的测试框架也无法使用
|
||
- 尝试Rust FFI(C语言绑定)
|
||
- [Rust-Bindgen](https://rust-lang-nursery.github.io/rust-bindgen/)
|
||
- 已经对xv6生成了绑定
|
||
- 但它依赖std库,不好集成到只依赖core的RustOS中
|
||
- 另一个移植思路是用C绑定Rust,把原有的模块逐个用Rust重写,对C提供临时接口,保持OS始终完整
|
||
|
||
#### 2018.04.07:ACPI
|
||
|
||
初步完成ACPI的移植
|
||
|
||
之后的底层驱动部分,考虑 找Rust库 > 从RustOS里摘 > 从xv6移植。
|
||
|
||
### 第6周
|
||
|
||
#### 2018.04.04:RISCV
|
||
|
||
找到了RISCV Rust Toolchain的Docker,编译样例项目失败,看上去除了作者本人,还没有别人成功过。
|
||
|
||
#### 2018.04.02:32位boot
|
||
|
||
在blog_os x86_64框架下实现x86的boot
|
||
|
||
#### 2018.04.01:RISCV
|
||
|
||
构建RISCV32/64 Docker
|
||
|
||
尝试构建RISCV Rust Toolchain,失败 |