Lecture 1: O/S overview
-
fork、僵尸进程、孤儿线程
fork作用就是把当前进程的内存块复制一份,当然也包含了程序计数器,所以fork出的子进程是从fork语句下面开始执行的。
僵尸进程的定义是执行完但是还没回收的子进程,其PCB还在进程表里留着。
实际代码中就是子进程没被wait。wait除了等待子进程的作用外,还有回收子进程的作用,也就是把子进程PCB从进程表里删了。
wait = wait + delete
no wait = no delete
但是为什么要有僵尸进程?为什么死掉的子进程PCB还要留着?
创建进程的唯一系统调用是fork(),也就是进程和进程之间是树形的结构。
父进程下一步可能取决于执行完的子进程的退出状态和资源使用情况,比如子进程是否异常退出,子进程的CPU使用率等等。
孤儿进程的定义是子进程还没执行完,父进程就执行完了,那么子进程就成了所谓的孤儿。
但是不用太过于在意,init进程会收养这些孤儿,并定期调用wait()。
所以最好fork出来的子进程要用wait去回收,虽然无伤大雅,但是子进程迟迟不销毁也挺麻烦的。
-
文件描述符
JAVA的NIO里所谓的channel就是文件描述符,文件描述符就是一个整数,每个文件描述符对应一个文件。
每个进程诞生的时候,都会有一个fd列表,初始化三个fd,0是输入文件,1是输出文件,2是报错文件。
此后再open一个文件,其fd从4开始递增。
如果使用close释放了这个fd,那么新的fd会占用这个释放的fd。例如当前进程fd列表为123456,释放了4,列表为12356,那么新open的文件的fd就为4。
当然,不同进程的不同fd也可能指向同一个文件。比如进程A的6和进程B的16,可能都是指向XXX.txt
-
管道
没什么好说的,C语言中pipe方法可以获得输入和输出的fd,fork方法再次pipe又是一对输入和输出的fd。由于是单向通信,所以这四个fd,只要对其中两个操作即可,另外两个记得要close。
另外,在linux指令中,比如
ps -a | grep xxx
, 竖线之前的输出结果,会作为竖线之后输入的结果,也是管道。
Lecture 2: Operating System Organization
-
微内核和宏内核
宏内核把OS所有功能全都封装在内核态,好处是性能更强,坏处是代码多容易出bug。
微内核把OS部分功能外包到用户态,比如文件系统、sh,好处是更稳定,坏处是内核和用户态多次跳转,性能差。
OS紧张的情况,比如作为服务器,追求性能会用宏内核。
嵌入式系统比如Cell、Minix,追求稳定会用微内核。
Lecture 3: Page Tables
-
3.1 Page Table
这里就说一下个人对页表的理解。
首先,各个进程都要申请内存资源,如果进程A申请了0~1024的物理地址,进程B也申请了0~1024的物理地址,那就寄了啊,总不可能让每个写代码的人去相互协调。
页表就是为了进程之间资源分配的相互隔离。
可见物理地址是无法直接让进程使用的,所以这里有了虚拟地址的概念。
多少位的操作系统就决定了寻址空间有多大,目前64位操作系统的寻址空间是2^64,基本可以看做无限大。也就意味着进程可以申请2^64内任意大小的空间。但这只是虚拟的。
页表的作用,就是把虚拟地址转换成真实的地址。
以XV6举例,64位地址中,只有39位被用来作为虚拟地址,其中12位是页内偏移offset,4k作为一个page,27位是页表索引index,一个进程可以有2^27个page,对应的页表是2^27个page table entry(PTE)。
页表本质就是PTE数组。PTE数组的key是虚拟地址,所以数组的长度是2^27,value是一个44位的physical page number(PPN),物理地址就是PPN + 12位offset,所以物理地址是56位。
这个时候问题来了,2^27到底有多大?
答案是2^24个B,也就是2^4个MB,也就是16MB。每个进程都要16MB的空间拿来做转换就太离谱了,纯纯的浪费,无法接受。
为了解决这个问题,就有了多级页表。
说多级页表之间,先说一下一个简单的数学问题。100个数字能否给2500个桶做标记。学过二元一次方程的初中生都知道可以。
那么运用到页表中,2^27个index也没必要那么实诚的申请一个2^27长度的数组,申请3个2^9长度的数组也可以解决嘛。
3个2^9长度的数组,占用的空间为3 * 2 B,仅仅为6B。三个数组就是所谓的三级页表。
第一级页表存储第二级页表的物理地址,第二级页表存储第三级页表的物理地址,第三极页表存储真实物理地址。
3.2 Kernel address space
每个进程都有一个页表,是用来描述用户空间的,而内核空间是进程共有的一个单独的页表。
Lecture 4 RISC-V calling convention
4.1 ISA & Assembly Language
CPU架构目前最出名的就是ARM、Intel x86、RISC-V。
指令集分为两种:RISC(Reduced Instruction Set Computer)、CISC(Complex Instruction Set Computer)。
ARM全称是Advanced RISC Machine,基于RISC,追求节能,低功耗,相比X86性能较差,多用于手机平板。
X86泛指一系列基于Intel 8086且向后兼容的CPU架构,基于CISC,主要追求性能,但会导致功耗大,不节能,多用于Intel和AMD的PC机。
另外,因为数字无法注册商标,所以该架构Intel和其竞争者用了其他名称,比如奔腾(Pentium)、酷睿(Core)、锐龙(Ryzen,AMD推出)
ISA:Instruction Set
C -> Assembly(.S/.asm) -> binary(.o)
汇编语言就是一行行指令,基于寄存器进行操作,而不是基于内存操作,寄存器不够用了,才会把数据放到内存操作。
Lecture 5 Traps
5.1 trap
trap有三种情况:
- syscall
- exception
- 设备中断