Skip to content

TrustOS

此部分是Undefined-OS与TrustOS的分析与比较。

相关链接

简介

TrustOS是由华中科技大学RustTrushHuster队21级学生郑卯杨,张晨皓,翁哲宇使用rust语言编写的宏内核操作系统。

TrustOS在rCore-Tutorial Chapter6的基础上,重构内存管理,实现多线程并发控制和信号机制,移植对接LwExt4文件系统,添加修改系统调用以满足POSIX标准,并实现了文件缓存,块缓存,内存懒分配等优化。

TrustOS由rCore原有的11个非POSIX的syscall拓展到105个满足POSIX标准的syscall。

分析和比较

整体了解了以下他们仓库的代码结构和提交历史,他们的文档写的很详细,对OS的各个方面都介绍的很仔细,也便于我进行比较。

总体来说,他们实现的syscall数量和我们相近,相较于我们的OS,他们的优点在于:

  • 实现了内存页的COW机制
  • 实现了文件IO缓存机制

他们在内存管理方面实现的更为完备,这是我们需要学习和改进的地方。

写时复制(COW)

写时复制, 又叫COW, 是指在复制内存的时候, 不真的拷贝内存, 只是将新的虚拟地址映射到同样的一块物理地址, 直到程序要进行写操作时, 才进行实际的拷贝操作。这样做的好处是如果相应的数据在复制到消亡的过程中并没有进行过写操作的话, 则可以节省拷贝这部分数据的时间, 在fork+exec操作时尤其明显。

TrustOS操作系统对ELF段、堆和文件映射区这三部分实现了写时复制。在PTE的10个标志位中, 有2位是保留供操作系统使用的, 他们将第九位作为COW的标志位, 表示此页面是通过COW共享的。

在进行fork的时候, 对于需要COW的段会进行如下的操作:先遍历父进程的所有页面, 然后修改子进程的pagetable, 使得子进程的虚拟地址能映射到对应的物理页。之后, 修改父子进程的标志位, 将写标记清除, 将COW标记置一, 这样当COW页面发生写操作时就会触发pagefault进行处理,此时可以通过COW位来辨别是否是由写时复制触发,需要对物理页帧进行复制, 同时修改页目录中的页表项, 使页表项指向新的物理页。

懒分配与文件映射

懒分配, 又叫lazy allocation, 是指当用户向内核申请空间时, 内核并不实际分配空间, 而是等到用户访问了这块空间时再分配, 这样可以有效减少物理空间的浪费。

TrustOS在堆段、栈段和文件映射区实现了懒分配。

堆段的懒分配和文件映射区的懒分配有些不同, 当堆段触发懒分配时, 只需要直接从物理页中分配一个可用页即可。而文件映射区, 它是映射了文件的, 因此它在分配了物理页后, 还需要从文件中读取相关的内容进行保存。因此在结构体 MapArea 中有一个 file 字段, 就是用于存储mmap对应的文件的句柄, 当处理懒分配使可以从对应的文件中读取数据代内存的相应位置。

懒分配还有一个需要尤其注意的点就是,对于一个懒分配的区域,用户可能还没有访问就直接用来作为承载系统调用返回信息的容器,那么对于这段区域的首次访问实在内核空间进行的,此时并不会跳转到内核空间的中断处理函数,从而引发panic。所以对于此类系统调用,往用户空间写值时需要事先判断对应的页是否确实已经分配了,如果没有分配的话,先对对应的页进行分配后再进行写操作。堆段和栈段读操作不会有这个问题,因为必定是在用户空间已经进行过写操作的地址才会需要在用户空间读。而文件映射区的话对于读操作也需要考虑这个问题,因为他的内容可以来自于文件,不一定需要用户进程去访问。

启示

TrustOS在内存管理方面设计的比较完善,综合考虑了COW,懒加载,文件映射和它们综合到一起的情况。相比之下,我们目前的内存管理还有一些缺陷,不仅仅是效率上的问题,还有一些正确性上的问题。

目前我们的OS尚不支持COW功能,对VFORK的支持也不好。此外,如果mmap映射到文件,我们仅支持只读映射,且会一次性加载整个文件到内存。因此,我觉得我们的OS的改进路线是:

  • 支持VFORK。目前我们在 sys_execve的时候是直接将原本的内容unmap,这存在两个问题:一方面是在execve失败时将无法返回原应用程序,另一方面是无法支持类似VFORK这样的fork + execve优化。我准备实现成,在execve的时候创建新的地址空间,然后加载新的程序,如果成功就解除对原地址空间的引用,这样可以支持使用了 CLONE_VM标记创建的进程正确执行execve,同时避免了对原进程内容的拷贝,减少了内存开销,提高了运行速度。
  • 支持mmap的写回。目前如果通过mmap将文件映射到内存,且被标记为 SHARED,那么这个内存段在取消映射时不会被写回到文件中。我的想法是添加一个新的Backend来处理这个问题。
  • 支持mmap映射文件时的懒加载。目前如果通过mmap将文件映射到内存,我们会将整个文件读入到内存中。在文件很大的时候是非常低效的,且会占用大量的内存空间。这和上面的想法一样,我准备在新的Backend中正确处理这个问题。
  • 完整的COW支持。目前需要用到COW的场景主要是fork,我们可以在fork的时候对页表和映射信息进行处理,确保新进程的地址空间映射到的物理页面和原进程相同,同时没有写权限,在执行写操作时我们再进行物理页面的复制。