让你了解操作系统基本概念
范培华 2016-05-17
前言:本文是《操作系统教程(陈怀临注释)》的读书笔记,陈首席是在原书 pdf 图片上注解,字体
比较模糊,故我把注释中觉得比较重要的片段摘录下来。读完此文可以让非技术人员对操作系统有框架
性的认识,也可以唤起技术人员记忆中某些概念片段,实际上很多概念我也理解得有点模糊,大家一起
学习。
1、OS 其实是概念多于理论,技术多于算法。因此把握 OS 最重要的是把握概念,特别是概念的层次化。
2、OS 的本质是实现了 CPU, Memory 的虚拟化。现代的虚拟化技术是再次虚拟化,实现了 OS 的虚拟化。
3、在经典 Unix 下,kernel 是共享的,因此必须互斥。kernel 属于每个(each)32bit 进程地址空间的
一部分,例如 3~4G 部分。
4、Page on demand 是理解 VM 虚拟内存的 Rule 1(Swap)。
5、在经典 OS 设计中,例如 Unix, “Everything is a le” 是非常重要的设计原则。任何一个外设,
最后都通过文件系统来表达。一个通过 open 得到的文件句柄可以唯一的定位一个设备(块设备 or 字符
设备),并可以通过文件的 read/write 来操作。
6、初学操作系统的大学生通常会对文件句柄(File Handler)有点迷惑。可以这样理解。就是 open()
的时候,操作系统为你构建一个表
项的数组的下标。这样也就理解了一个进程可以打开的文件数目是有上限的。为什么?数组的大小是固
定的,除非改参数。
7、OS 最重要的概念就是进程( Process)。可以理解为是操作系统“管理 ”的最小单位。虚存
(VM),文件( File)都属于(Belong to)这个进程的 domain。例如,属于哪个进程的。进程就是
一个在运行中的程序,通常是一个 ELF 的加载。
8、学习文件系统的时候,不要去纠结驱动程序的实现。类似谈恋爱,非要大家都坦白从幼儿园开始的情
史,是大家彼此过不去。要学会“透明”。概念到文件系统,就刹住。否则,为了理解文件系统,非要
把 INT13 通读,是没有必要的。文件就是文件。
9、文件系统最重要的是控制块( Control Block)。要知道数据(例如, 512B)在硬盘哪个地方。而
且要靠指针串起来。例如,早期 DOS 的 FAT 表都是这个目的。在现在分布式文件系统中,称为 metada
ta。目的都一样:在哪里。 metadata 或者 control block 失效了,数据都无法定位了。
10、基础 教材通常会有意识 的凸显概念。其实任何概念本身就是抽象和总结 出来的。什么是 “ 虚拟处
理器”。说白了,就是每个进程数据结构里的 CPU 相关寄存器的值。那就是对于那个进程而言,她的虚
拟处理器。虚拟内存?就是她的,例如,页表和 MMU 的设置。
11、初学 OS 的同学不要去过分理解虚拟处理器这个概念。还是应该从经典分时系统出发。现代 OS 的本
质是分时。其他都是演变出来的。分时就是大家皇帝轮流做。因此下台的时候要保存一些状态。等下次
轮到时,从上次断的地方重新来。
12、输入输出( I/O)的访问必须串行化(Serialization),否则就乱了套。 why?写过驱动就知道,
控制设备的那些 control register(控制寄存器)还没有完成一个操作,如果被覆盖,设备就死机或
者 reset 了。并发是 CS 许多算法的目标,但底线是:和串行语义要一致。
13、操作系统另外一个重要任务是参与和指导 CPU 设计。现代 silicon design 从来都是 co-design。
否则,硬件工程师都不知道在干嘛。不能画电路图玩吧。真正懂一个 silicon 的必须包括 OS architect。
这也是为什么 OS 是计算机科学 or/and 工程的美丽之花。
14、在单 CPU 的年代,除了中断(时钟,外设),一个计算环境不存在并发。 OS 做调度也是在几个固
定的点,例如, timer,syscall ,wait for I/O 等。
15、MultiTasking 的本质就是大家共享资源例如, CPU。例如,通过分时,或者是主动退让( Yiel
d)。多道( Multi Tasking)和多重(Multi Processing)处理的区别是: multitasking 就是一个
CPU,例如。 multiprocessing 是多个 CPU。现在的多核,多(硬件)线程都属于这个范畴。 MultiTa
sking/SingleCPU 本质上还是串行化的(Serialized)。
16、在学习操作系统的时候,一个重要的概念是传统操作系统内核是独占,不可剥夺的, Kernel is not
preemptive。这个概念的理解把握对阅读源码,理解 Unix/Linux 的演化是至关重要的。对锁机制,
锁粒度的优化也是最重要的。
17、用户态/核心态的本质是: 保护。保护什么? Kernel 的全局变量。为什么? Kernel 是共享的。每
个进程,例如, 32 位系统 Linux,是 4G 空间。3G 用户+ 1G 核心 =进程。因为是共享的 kernel,所
以需要互斥。否则,全局变量用一半就被冲了。
18、理解 kernel 空间是 PART OF 一个进程空间,是对现代操作系统把握最重要的概念之一。例如,经
典 OS 有一个重要的 statement:kernel 是没有 context 的。什么意思? kernel 不存在生命。是属于
一个进程的,而且是共享的。
19、系统调用是操作系统里略微难理解的一个概念。其实就是通过一个特殊指令,使得 CPU 陷入到异常
处理,然后通过查表(事先填好),最后调用相应的 kernel 库函数。(在经典 os 里), kernel 就是全
局变量+函数。写系统调用时,要注意的是对参数传递的约定要比较清楚。
20、操作系统的发展经历了单一内核( Monolithic Kernel)和微内核(Micro Kernel)的学术争论。
最经典的是 Linus 和操作系统泰斗 Andres T 1992 年的辩论 ( http://t.cn/S4dT62 –
Torvalds_debate )。 现在基本上是 convergence,融合了。特别是在虚拟化技术的今天。
21、进程是最小管理单位;( System Scope)线程是最小调度单位。同一个进程的线程序共享内存,
例如全局变量。通常说的线程在 kernel 里对应一个调度 object,通常称呼这样的 thread 叫做 System
Scope。如果是 Appliacation Scope,叫做用户线程,在 kernel 里不存在 entry
。
22、在理解操作系统的时候,内核( Kernel)是属于一个进程(Process)空间的一部分 是一个重要的
概念。你编的代码+内核 构成了一个进程空间。
23、操作系统从过去的 Monolithic OS 与 Micro Kernel 的学术和工程对立,发展到互相融合。特别是
最近的虚拟化技术的发展,使得已经无人再去纠结这些结构之争。 CPU,多核技术的发展也是导致这些
区别淡化的重要原因之一。
24、学习 OS 的时候,会接触 CPU,例如 x86. 其实 x86 不太适合做 CPU 教材例子。 IA32 为了支持许多
历史设计含有了许多老东西,例如段地址等。现代 CPU,包括 Intel ,都已经是采用 Flat Memory 结构。
学生们没有必要去知道和理解之前的技术。应该直接一步到位。
25、中断/异常处理是掌握一个 CPU 重要的部分。不同的 CPU 实现都有细微的区别,但本质都类似。
保护现场,处理中断/异常,根据情况决定是恢复现场执行,还是 reboot CPU。
26、信号在 Unix 中比较绕,是异步的通知,进程之间通过互相发 kill()。也可以是 kernel 发给一个进程。
有的 signal 不能被捕获,进程必须消亡;有的可以自己接管,接着运行。看是否有 signal 的时机需要很
清楚:异步方式:在系统调用,中断返回的时候。
27、如图所示, 操作系统从中断,异常,或者系统调用进来,要结束的时候,都存在一个检查点(
checkpoint),看看是否需要调度,是否需要处理软中断( signal)。
28、中断处理程序要求的是快速。因此 Linux 里有一套完整的 Bottom Half 的机制来做优化,把不存在
重入危险的,不是那么急需立刻需要处理的,可以通过 Bottom Half 的一些手段,来“延迟 ”执行。从
而使得整体系统并发度提高。早期 BH,TaskQueue 机制都已经不用了。
29、在学习 2.2 中断技术方面,可以不要太陷入 Linux 独有的中断技术细节中。大概了解一些概念就可以
了。把握两个重要的概念:中断上下文;进程上下文。中断处理程序需要快,然后迅速开中断,防止系
统堵塞。这是 Linux 各种各样的 Bottom Half 机制的由来。
30、传统 Unix 的内核是非剥夺式的调度( non preemptive),目的是简单从而保证内核数据结构一致
性(内核是各个进程共享的地址空间)。现代 OS,如 2.6 之后的 Linux ,可以支持内核 preemptive
schedule 了。目标是支持高并发系统。
31、fork, pthread_create 最后都是调用 clone()函数,传递进去的参数不同而已,子进程/线程在内核中
都是产生一个 task_struct 元素。"CLONE_THREAD"参数可以告诉 clone 调用,这个新产生的 object 是
个 thread 逻辑。从而在 thread_group 里会把这个新产生的 object 加入,作为一个线程来对待,但这种
对待是一种逻辑理解而已。
32、例如,一个时间很长的读磁盘的操作。在阻塞 sleep 的时候如果是可中断的,另外一个进程可以发
signal,这样这个进程就能够从这个读数据的系统调用中退出来了。
33、为什么需要线程( thread)来支持并发?都用进程不就可以了嘛?因为属于同一个进程的多个线程
是共享全局变量的,共享内存的,可以合作来完成一个任务。而如果拆分为多个进程,通常就需要 IPC
通信才能完成这样的任务。这就是线程的好处。
34、