Mit6.828/6.S081 Fall2020 学习记录

为什么要学习这个课

Mit6.828 是著名的操作系统课程,为了完成用Nexys4 DDR 制作一台计算机[2] 操作系统,计划把它过一遍
QQ--20210323164948-1
这门课主要由讲义和lab组成,核心是lab。所以我这里的学习就以lab为主,参考讲义,边做边学
讲义 xv6的英文版在这里
中文版在这里

内容概览

一共11个LAB
QQ--20210325094116
以及环境配置

环境配置

QQ--20210324141250
如果安装debian的话,需要安装测试版(bullseye),buster版本的qemu版本过旧不能运行它提供的程序。
安装好debian bullseye 后需要安装 QEMU(虚拟机)和 GCC riscv工具链,直接用它提供的脚本一行就可以完成。(需要注意Fall2019使用的QEMU版本是4.x 而这个脚本安装的始终是最新的QEMU(已经更新至5.x),在Fall2019项目中make qemu会报错)
最后再git clone Xv6,进入目录,make qemu即可。
QQ--20210324142452

LAB 1: Utilities

了解应用层调用

sleep

进程睡眠n个tick(一个tick时间100毫秒)

pingpong

用fork和pipe创建父进程和子进程,互相发数据

primes

用fork和pip创建多个进程来计算质数

find

查找指定目录下的文件

xargs

为不支持管道的程序支持类似管道的功能

LAB 2: Syscall

了解kernel和user之间交互用的syscall

trace

输出选定范围的Syscall

sysinfo

输出系统剩余的内存空间和运行的进程数量

LAB 3: Pgtbl

了解Riscv架构下的页表和Xv6对于页表的管理

vmprint

输出页表里的所有页表项PTE(page table entry)和它们的物理地址

A kernel page table per process

应用程序的userpagetable都是从0开始到sz顺序存放的,所以要把userpagetable中的页表项复制到kernelpagetable只需要按proc的sz大小来确定计算地址

simplify copyin/copyinstr

做完上面一个步骤,这部就比较容易,核心是实现一个从用户页表复制所有pte到用户的内核页表的一个函数,用户页表是从0开始到sz*PGSIZE的地址,只需要按顺序一页一页存就好,需要注意虚拟地址不可以超过PLIC的地址(plic是RISC-v架构下的中断控制器,超过了这部分就会被覆盖),并且要把复制进内核的PTE的PTE_U flag置0,使得操作系统可以在内核态对用户程序的数据进行访问
注意这个lab的测试运算量比较大,如果用虚拟机需要把虚拟机配置调高或者把测试的超时时间调长,不然有机会超时不通过

Lab4 Traps

这部分主要学习目标为熟悉xv6对于中断和异常的处理过程

RISC-V assembly

这部分主要用于熟悉RISC-V的汇编指令
这本RISC-V 手册有助于我们理解

Backtrace

实现一个追踪错误位置的函数

Alarm

实现一个定时调用用户函数的syscall

Lab 5 lazy page allocation

把xv6的页表分配从 请求多少页就真的分配多少页 改成 用户程序真的使用某一页再分配
通过减少分配页表数量,提高xv6的运行速度

Eliminate allocation from sbrk()

修改sbrk()函数,仅仅修改程序的用户页表大小,而不是真的分配内存

Lazy allocation

修改中断以及uvmalloc()\uvmunmap()等函数来完成每次接到缺页中断就分配给用户程序一个页表,以及保证页表移除正常工作

Lab 6 Copy-on-Write Fork for xv6

这章主要是实现讲义里说的父进程与子进程共享内存页表的方法,虽然只有一个hard级别的任务,这部分大概花了3天多时间完成
这部分最难的地方还是多结合讲义和Risc-V参考手册还有xv6源代码,把整个进程分配页表以及释放页表的过程搞明白
我遇到比较坑的地方是实现一个引用计数来释放页表的时候,应该把减计数器的操作直接放在释放页表的函数kfree()里,不这么做的话很难把所有需要减引用计数的地方找到,导致跑usertests的时候会有难以定位的错误

Lab 7 thread

Uthread: switching between threads

实现一个用户级的线程

使用gdb来调试xv6

1.在xv6根目录下启动一个命令行1,运行 make qemu-gdb ,会显示一个tcp地址
2.在上述位置再启动一个命令行2,运行gdb-multiarch (multiarch是跨平台的意思,如果使用普通的gdb的话默认硬件架构是主机的架构 通常是x86,而这里需要的是riscv的,需要进一步配置)
3.在2号命令行下运行 target remote:xxx (xxx为命令行1显示的tcp地址)
4.开始调试

Using threads

熟悉和使用UNIX的pthread库函数,实现多线程的时候加锁防止数据写入出错

Barrier

使用pthread库实现一个Barrier,简单说是让每个线程在这个函数里等待直到所有线程已经运行到这个函数里,然后全部线程一起唤醒。和xv6的sleep wakeup是同一种原理

Lab 8 locks

这部分熟悉xv6的锁机制,介绍了spinlock和sleeplock

Memory allocator

将页表分配的锁从所有cpu一个改为一个cpu一个

Buffer cache

将Buffer的锁从公用一个改成使用哈希函数并行多个,难点在于把原来的bcache改成哈希散列的

Lab 9 file system

熟悉xv6的文件系统

Larg files

inode的数据块链接增加二级索引,这个设计和之前操作系统课设做的ext2文件系统几乎一模一样

实现软链接,支持10层以内的迭代,这是我第一次亲手实现这个功能,之前都是从操作系统书上看到的

Lab 10 mmap

实现UNIX里的mmap和munmap。
我自己的理解mmap的作用是类似write和read,并且mmap可以更加方便的直接操作数据,不需要多次write或read操作
难点在于两个函数既需要操作内存页表,也需要操作文件系统,并且也需要考虑加锁的问题

Lab 11 networking

正常参考给的文档即可
在切换网络和电脑设备后通过了

GitHub链接

xv6-labs-2020

参考链接

MIT 6.828 | Star Trail