第一章 温故而知新
硬件架构
计算机三个中最关键的部件:CPU、内存、IO控制芯片
软件架构
每一个中间层就是对它下面那层的包装和扩展。
操作系统做什么
操作系统功能
- 提供抽象的接口
- 管理硬件资源
计算机的硬件处理是有限的,不能让CPU空转打盹浪费资源。
- 多道程序 multiprogramming
- 分时系统 time-sharing system
- 多任务系统 multi-tasking
- 抢占式内核
设备驱动就是对硬件的管理和抽象。
内存,如何将计算机上有限的物理内存分配给多个程序使用。如果使用绝对地址访问,存在如下问题:
- 地址空间不隔离
- 内存使用效率低
- 程序运行的地址不确定
提出来分段的概念,芬顿就是把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间。
分段解决了上面所述的问题1和问题3。
所以又提出来一个概念分页,分页解决了上面所述的问题2.
分页就是把地址空间人为的等分成固定大小的页。
当把进程的虚拟地址空间按页分割,把常用的数据和代码页装载到内存中,把不常用的代码和数据保存到磁盘中,当需要用到的时候再把它从磁盘中取出来。这样就是实现了内存效率的高效。
这部分的功能是由操作系统的MMU单元来完成的。
线程
线程是程序执行流的最小单元。
使用多线程的原因:
- 某一个操作可能会陷入等待,等待的线程会进入睡眠状态,无法继续执行。多线程执行可以有效利用等待的时间。典型的例子是等待网络响应,这可能花费数秒甚至更长时间。
- 某个操作会消耗大量的时间,如果只是一个县城,程序和用户之间的交互会中断。多线程可以让一个线程负责交互,另一个线程负责计算。
- 程序逻辑本身就需要并发操作。例如一个多端下载程序。
- 多CPU或者多核计算机,本身具备同时执行多个线程的能力,因此单线程程序无法全面的发挥计算机的全部计算能力。
- 相对于多进程应用,多线程在数据共享方面的效率要高很多。
线程的访问权限
线程调度与优先级
在线程调度中,线程通常分为三个状态:
- 运行态,此时线程正在执行
- 就绪,此时线程可以立即运行,但是CPU已经被占用
- 等待,此时线程正在等待摸一个事件发生,无法执行。
线程调度有很多算法,优先级调度、轮询法等。
根据线程等待的时间,可以把频繁等待的线程称为IO密集型线程,把很少等待的线程称为CPU密集型线程。
在优先级调度环境下,线程优先级的改变有三种方式
- 用户指定优先级
- 根据进入等待状态的频繁程度提升或者降低优先级
- 长时间得不到执行而被提升优先级
可抢占线程和不可抢占线程
CPU会为每一个线程分配时间片,在时间片用尽之后,会被强制剥夺继续执行的权利,进入就绪态,这就是抢占。
在不可抢占线程中,线程主动放弃执行无非两种情况
- 当线程试图等待某件事件
- 线程主动放弃时间片
linux的多线程
fork可以快速创建新线程,使用的是写时复制的机制。
线程安全
竞争与原子操作
多个线程访问一个共享数据时,可能会造成不同步的问题。
原子操作,要么成功,要么失败
同步与锁
- 二元信号量,只有占用和非占用状态,只能被唯一的一个线程独占访问。
- 互斥量,信号量在整个系统中可以被任意线程获取并释放,也就是说同一个信号量可以被线程A获取,由线程B释放。
- 临界区,临界区的作用范围仅限于本进程,其他进程无法获取该锁。
- 读写锁,读状态可以共享,写状态唯一。
- 条件变量,线程可以等待条件变量,线程也可以唤醒条件变量。
可重入与线程安全
一个函数可被重入,只有两种情况,一是多个线程同时执行这个函数,二是函数自身调用自身。
一个函数可重入,必须具备以下特点:
- 不使用任何局部静态或者全局的非const变量
- 不返回任何局部静态或者全局的非const变量的指针
- 仅依赖于调用方提供的参数。
- 不依赖任何单个资源的锁
- 不调用任何不可重入的函数
过度优化
volatile关键字试图阻止过度优化,阻止编译器为了提高速度将一个变量缓存到寄存器而不写回。
barrier,CPU提供的指令防止指令被交换顺序。
多线程内部情况
三种线程模型
- 一对一模型,一个用户使用的线程就唯一对应一个内核中使用的线程。
- 多对一模型,多个用户线程映射到一个内核线程上。
- 多对多模型,多个用户线程映射到多个内核线程上。
完---------------------