推荐一门课:6.828

这篇博文的目的是向大家推荐一门课:MIT的6.828

这门课主要讲的是操作系统原理与实践,具体分三个部分:lectures,labs and readings。 Lectures部分会通过介绍xv6来阐述OS原理,并解读xv6源码; Labs是这门课的实践部分,课程会提供一个OS的框架,但核心部分全部都是缺失的,需要学生来填写核心代码来实现这个OS; Readings部分会阅读一些paper来了解一些更深入、更有意思的topic。

一个很自然的问题是,它与别的OS课程的区别在哪里? 最大的一个区别是,这门课的实践部分比重非常非常大,而OS本身就是一个实践学科,所以6.828的编程练习会让你对OS的概念非常清楚。 具体来说,这门课一共有7个lab,写完这7个lab,一个操作系统就被写出来了(名字叫JOS,是专门为这门课设计的Exokernel,麻雀虽小五脏俱全),除此之外,老师们为了让学生了解不同OS的架构和风格,在讲课阶段会主要以xv6(一个教学的操作系统,它是Monolithic kernel)的代码讲解OS概念,也就是说,在学期末,你会自己编写一个Exokernel和完整阅读一个Monolithic kernel的代码。

虽然我本科阶段也上过OS,但大作业的量完全没有到达这个编码强度(当时交大的OS课大作业是编写一个toy文件系统),导致自己以前对很多OS的概念和实现细节都是只见树木不见森林。然后从去年12月初,一共用了三个月的时间把这门课刷完了,对OS的理解清楚了很多,以及本科学的很多知识点都串了起来。

基于这个原因,不管你是学生还是已经工作几年的前辈,如果你想了解一个小型OS具体到代码是如何实现的,这门课是首选。

介绍一下每个Lab的需要做的事情:

  • Lab1是熟悉的过程,需要学习QEMU模拟器的使用、开机启动流程、调试工具、bootloader、以及整个加载kernel的流程。做完这个lab会具备基本的内核调试能力,以及掌握开机到通电,bootloader是如何加载kernel的。

  • Lab2要完成JOS的的内存管理模块,需要学习一些计算机基础知识,如虚拟地址系统是如何工作的,地址空间是如何切分的,物理页面是如何管理的。做完这个lab将会给JOS添加最基本的内存管理功能,即Kernel其余模块需要物理页,这个模块可以分配出来。

  • Lab3为JOS添加进程的支持、异常/中断的支持、系统调用和页中断的支持。这个lab内容比较多,但收获也比较大,做完后会对从用户态陷入内核态,执行系统调用,然后返回这整个流程都非常清楚(不是泛泛的清楚,而是代码级别的清楚,这是和学概念不同的地方)。

  • Lab4为JOS添加多核支持、RR调度、COW的fork、抢占式内核、时钟中断和最基本的IPC机制。做完Lab3和Lab4,一个能用的OS已经出来了,但用途非常有限,因为没有文件系统和网络的支持,Lab5和Lab6就会做这两件事情。

  • Lab5为JOS添加基于Disk的文件系统、块缓存、实现键盘中断、修改一个shell支持最基本的功能。完成这个Lab后就可以在shell里面输入命令以及文件系统的支持了。JOS没有实现crash recovery,在xv6用log的方式实现了crash recovery,代码都非常好(虽然效率很差…课里会讲)。

  • Lab6为JOS添加网络的支持。网络部分主要分两个大块,一个是协议栈的编写,一个是驱动的编写。协议栈太复杂了,于是课程提供了现有的lwip库。我们需要实现驱动,这涉及到阅读intel的网卡硬件手册,这是本课程让人头痛的地方之一。完成驱动以后,需要完成用户态的network server(网络服务以用户进程的形式提供是Exokernel的特点之一,和Microkernel很像),还需要完成一个用户态的web server,完成之后经过QEMU的端口转发,在host机器可以访问qemu里面运行虚拟机的web server!第一次运行成功也是觉得非常神奇,因为OS从上到下,在此时此刻,全部打通了,可以向外提供服务了,并且这个OS的核心代码全部是在课程里完成的。

  • Lab7和Lab6只需要选择一个做,Lab7是一个需要组队的开放式课题。

不可否认的是有些lab真的非常让人头痛、一个bug可能会调很久甚至几天没有进展,但到最后收获的远多于付出。

如果你对上面的Lab有兴趣,那花三个月去上一下这门课会非常值。其实可能用不上三个月,我是跟着课程进度一点点做的,如果有一点OS基础可以直接做Lab,稍微加班加点一个月左右也可以完成了。

JOS还有很大的提升空间,归根结底这只一个“working”OS,离工业级使用还差很多,以下是我总结的一些改进点:

  1. JOS和xv6的内存管理方式都是空闲链表,这导致如果内核想要连续的物理内存,将十分困难(因为碎片的存在,即使没有碎片,也将是O(n)的复杂度),所以linux采用了buddy system来解决碎片问题。(至于内核为什么需要连续的物理内存,请看我在V2EX上问网友的一个问题

  2. JOS和xv6的进程调度是最简单的RR方式,这使得它们无法应用在某些特殊的场景下,而且父进程很容易fork子进程把CPU时间全抢了。

  3. JOS和xv6根本就没有磁盘调度算法,应用层来一个读写磁盘请求就直接由驱动发送给磁盘控制器。

  4. JOS的文件系统效率差,且不支持crash recovery,即使是实现了crash recovery的xv6,效率也非常差。不过在文件系统设计中,performance(don’t write the disk)和safety(write the disk ASAP)本身就是一个tradeoff。

上完这门课后推荐看一下Rober Love的《Linux内核设计与实现》,会发现JOS到底哪里做得不好以及Linux是如何解决的。

附:Lab的代码地址和上这门课时的一些笔记