最近愈来愈觉得要在做中学,所以这里记录一下小林 coding 教程中 个人认为 OS 里面对非底层(i.e. 不涉及系统编程的)程序员的重要概念。 部分重要图解同样转载至上文,侵删。
注意这些内容是个人认为【绝对不能忘】的内容,而非一个完整的 list。 知道这些 high-level 的设计概念之后详细设计再进行深入。 类比一下,你可以把这些东西称作你应该放在 register 或者 L1 cache 里面的东西, 但是碰到细节问题你还是需要 L2, memory, disk 的。 : )
CPU / Cache
- 存储器的层次结构为:
- Register
- L1, L2, L3 Cache (SRAM)
- Memory (DRAM)
- Disk (SSD / HDD)
- CPU Cache 每次访问数据(这个其实包括数据和指令)的时候都会缓存和其相邻的块。要充分利用这个 locality。
- 访问数据时,按照内存的布局顺序访问。
- 访问指令时,由于 CPU 存在分支预测,所以处理 collection 数据的 if-else 的时候,先 sort 再处理会更快。 (原因是分支预测器在 collection 是顺序的时候会根据历史命中数据对未来进行预测,这样命中率会很高。)
Memory
- OS 为进程提供虚拟内存,并有一个 mapping 机制将虚拟内存 map 为物理内存。
- 内存的分割分为分段(segmentation)和分页(pagination)。 它们可以合在一起使用,称为段页式内存管理。 i.e. 先把程序划分为多个有逻辑意义的段,再把每个段划分为多个页。
- Linux 内存布局如下。
- OS 会将没有经常用到的内存换出到物理内存之外,称之为 swap。
- 对于
malloc
而言,分配虚拟内存时不会分配物理内存。只有访问的时候才会触发 page fault,分配物理内存。 - 内存紧张/OOM 的时候内存存在一系列的回收机制。
Process
- 进程是一个运行中的程序。
- 进程理论上有如下状态(Linux 的模型有所不同,但是基本思维类似)。
- OS 通常会把阻塞的进程的物理内存 swap 到硬盘,需要的时候再换入。这个状态叫做挂起。
- PCB(进程控制块)是进程的唯一存在标识,包括其状态,内存空间,打开的文件的列表,I/O, 以及 CPU 的状态信息(用于进程被切换之后能继续执行)。
- 进程间的通信方式有 pipe,消息队列,信号量(semaphore),信号(唯一的异步通信机制),socket 等。
Thread
- 线程是进程中的一条执行流程,满足:(1)可以并发运行,(2)可以和同进程的线程共享相同地址空间。
- 线程切换快的一些原因:无需创建 PCB,虚拟内存共享(意味着无需切换页表), 数据传递无需经过内核(因为共享内存和文件资源)。
Mutual exclusion and synchronization
- 进程的协作可以通过锁和信号量实现。
- 经典的几个问题:Dining philosopher, producer-consumer
- 避免死锁最简单的方法就是资源有序分配法。(i.e. 两个线程用同样的顺序访问资源。)
- 理解概念:互斥锁,自旋锁,读写锁,乐观锁,悲观锁。详解
(未完待续)