MIT6S081
操作系统再学习
资源
MIT 6.S081: Operating System Engineering - CS自学指南 (csdiy.wiki)
- 课程网站:https://pdos.csail.mit.edu/6.828/2021/schedule.html
- 课程视频:https://www.youtube.com/watch?v=L6YqHxYHa7A,每节课的链接详见课程网站
- 课程视频翻译文档:https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/
- 课程教材:https://pdos.csail.mit.edu/6.828/2021/xv6/book-riscv-rev2.pdf
- 课程作业:https://pdos.csail.mit.edu/6.828/2021/schedule.html,11个lab,具体要求详见课程网站
course
evn
我在我滴腾讯云上搞的 怕把本地环境搞的乱七八糟,,,
LEC 1
进程和内存
1 | int pid; |
这里有个疑惑
1 | parent: child=1234 |
为什么这两个会没有顺序被打印出来?
Q: 我有一个子进程 应该都过了 pid>0 然后不是应该先打 parent?
我的我的 太长时间没学 变傻了
A: 执行顺序是这样的 但打印的时间是个比较玄幻的东西 主要看谁先打印完?
I/O 和文件描述符
PIPE
1 | int p[2]; |
HW
实现sleep
建议
- Before you start coding, read Chapter 1 of the xv6 book.
- Look at some of the other programs in
user/
(e.g.,user/echo.c
,user/grep.c
, anduser/rm.c
) to see how you can obtain the command-line arguments passed to a program. - If the user forgets to pass an argument, sleep should print an error message.
- The command-line argument is passed as a string; you can convert it to an integer using
atoi
(see user/ulib.c).
1 | int |
- Use the system call
sleep
. - See
kernel/sysproc.c
for the xv6 kernel code that implements thesleep
system call (look forsys_sleep
),user/user.h
for the C definition ofsleep
callable from a user program, anduser/usys.S
for the assembler code that jumps from user code into the kernel forsleep
. - Make sure
main
callsexit()
in order to exit your program. - Add your
sleep
program toUPROGS
in Makefile; once you’ve done that,make qemu
will compile your program and you’ll be able to run it from the xv6 shell. - Look at Kernighan and Ritchie’s book The C programming language (second edition) (K&R) to learn about C.
稀里糊涂看了下 chapter 1 , echo.c
1 |
|
成果
首先大家都用了这三个库 我们也用
然后需要考虑一下 这个参数改怎么写?
他的要求是
- 没输入参数要报错
- 传入一个 string,然后我们给他转成int类型, 然后该停多少秒是多少秒
- 使用系统调用sleep?
https://blog.csdn.net/zhaozhiyuan111/article/details/104050729 参考 argc argv
说实话 不是很会调试c,,,
哦哦 忘了改makelist 了 所以没打开,,,
。。。
最后改成这样 但是不知道为什么 他并没有sleep o.o… 好像是我的理解有点问题
这个 const 有点小难受,,,没太搞懂
ps: 还要抽时间写个 exit 我这个快捷键不知道为什么用不了,,,服了
1 |
|
不知道为什么 传进去的还是char 而不是const char
难道是因为我的const 加到了 数组上去?
。。。
用法错误 但我的代码为什么不sleep??????
没办法接着往后学了 说不定哪天回来看能解决(ps: 跑别人写的也不sleep
最后发现问题了 是时间单位的问题,,,
实现pingpong
https://blog.csdn.net/skyroben/article/details/71513385
写的很好
先整个小例子试一下
1 |
|
我的理解是 父进程中 pid 为 fork 生成的子进程的进程号 而子进程虽然fork了pid 但是值为0
简单写了个半成品 但出了点问题
fd[0] 是读 fd[1] 是写
他的要求是 父进程 先给子进程send byte 子进程接着要给父进程send byte
想了很久 确实需要两条通道? 先记录一下失败的案例 写着写着就乱了,,,
1 | int |
正确的
1 |
|
LEC 3
隔离性
如果没有操作系统,应用程序会直接与硬件交互
这并不是一个很好的设计。这里你可以看到这种设计是如何破坏隔离性的。使用操作系统的一个目的是为了同时运行多个应用程序,所以时不时的,CPU会从一个应用程序切换到另一个应用程序。我们假设硬件资源里只有一个CPU核,并且我们现在在这个CPU核上运行Shell。但是时不时的,也需要让其他的应用程序也可以运行。现在我们没有操作系统来帮我们完成切换,所以Shell就需要时不时的释放CPU资源。
进程本身不是CPU,但是它们对应了CPU,它们使得你可以在CPU上运行计算任务。所以你懂的,应用程序不能直接与CPU交互,只能与进程交互。
学生提问:这里说进程抽象了CPU,是不是说一个进程使用了部分的CPU,另一个进程使用了CPU的另一部分?这里CPU和进程的关系是什么?
Frans教授:我这里真实的意思是,我们在实验中使用的RISC-V处理器实际上是有4个核。所以你可以同时运行4个进程,一个进程占用一个核。但是假设你有8个应用程序,操作系统会分时复用这些CPU核,比如说对于一个进程运行100毫秒,之后内核会停止运行并将那个进程从CPU中卸载,再加载另一个应用程序并再运行100毫秒。通过这种方式使得每一个应用程序都不会连续运行超过100毫秒。这里只是一些基本概念,我们在接下来的几节课中会具体的看这里是如何实现的。
学生提问:好的,但是多个进程不能在同一时间使用同一个CPU核,对吧?
Frans教授:是的,这里是分时复用。CPU运行一个进程一段时间,再运行另一个进程。
防御性
这里的硬件支持包括了两部分,第一部分是user/kernel mode,kernel mode在RISC-V中被称为Supervisor mode但是其实是同一个东西;第二部分是page table或者虚拟内存(Virtual Memory)。
实际上RISC-V还有第三种模式称为machine mode。在大多数场景下,我们会忽略这种模式,所以我也不太会介绍这种模式。 所以实际上我们有三级权限(user/kernel/machine),而不是两级(user/kernel)。
每一个进程都会有自己独立的page table,这样的话,每一个进程只能访问出现在自己page table中的物理内存。操作系统会设置page table,使得每一个进程都有不重合的物理内存,这样一个进程就不能访问其他进程的物理内存,因为其他进程的物理内存都不在它的page table中。一个进程甚至都不能随意编造一个内存地址,然后通过这个内存地址来访问其他进程的物理内存。这样就给了我们内存的强隔离性。
User/Kernel mode切换
说的就是一个东西 将控制权从应用程序转到操作系统
例子如下
假设我现在要执行另一个系统调用write,相应的流程是类似的,write系统调用不能直接调用内核中的write代码,而是由封装好的系统调用函数执行ECALL指令。所以write函数实际上调用的是ECALL指令,指令的参数是代表了write系统调用的数字。之后控制权到了syscall函数,syscall会实际调用write系统调用。
编译运行kernel
首先,Makefile(XV6目录下的文件)会读取一个C文件,例如proc.c;之后调用gcc编译器,生成一个文件叫做proc.s,这是RISC-V 汇编语言文件;之后再走到汇编解释器,生成proc.o,这是汇编语言的二进制格式。
之后,系统加载器(Loader)会收集所有的.o文件,将它们链接在一起,并生成内核文件。
XV6 启动过程
还要回来重点学一下
HW
System call tracing
- Add
$U/_trace
to UPROGS in Makefile - Run make qemu and you will see that the compiler cannot compile
user/trace.c
, because the user-space stubs for the system call don’t exist yet: add a prototype for the system call touser/user.h
, a stub touser/usys.pl
, and a syscall number tokernel/syscall.h
. The Makefile invokes the perl scriptuser/usys.pl
, which producesuser/usys.S
, the actual system call stubs, which use the RISC-Vecall
instruction to transition to the kernel. Once you fix the compilation issues, run trace 32 grep hello README; it will fail because you haven’t implemented the system call in the kernel yet. - Add a
sys_trace()
function inkernel/sysproc.c
that implements the new system call by remembering its argument in a new variable in theproc
structure (seekernel/proc.h
). The functions to retrieve system call arguments from user space are inkernel/syscall.c
, and you can see examples of their use inkernel/sysproc.c
. - Modify
fork()
(seekernel/proc.c
) to copy the trace mask from the parent to the child process. - Modify the
syscall()
function inkernel/syscall.c
to print the trace output. You will need to add an array of syscall names to index into.
1 |
|
在三个文件中加上东西后 就可以成功编译了
但是
1 | $ trace 32 grep hello README |
并没有实现系统调用
sysinfo
要求实现
1 | struct sysinfo { |
实现两个函数
- To collect the amount of free memory, add a function to
kernel/kalloc.c
- To collect the number of processes, add a function to
kernel/proc.c
GDB调试
1 | make qemu-gdb |
再 主要是用了这个工具 gdb-multiarch
1 | gdb-multiarch |
即可
用的不是很熟,,不好玩
LEC 4
虚拟内存基本思想:
每个程序拥有自己的地址空间,这个空间被分割成多个块,每一块称作一页或页面(page)。每一页拥有连续的地址范围。
之前得知了几个启动时会调用的函数
kvminit:设置好虚拟内存
kvminithart:打开页表
LEC 9
就是硬件想要得到操作系统的关注。例如网卡收到了一个packet,网卡会生成一个中断;用户通过键盘按下了一个按键,键盘会产生一个中断。