温故而知新
CPU,IO,内存是核心的硬件。
理论上讲,增加CPU的数量会提高运行速度,且CPU的数量和运算速度成正比。但是我们的程序并不都能分成多个不相干的子问题。
操作系统的一个功能是提供抽象的接口,另外一个主要功能是管理硬件资源。
计算机的能力是有限的,无论你是否使用它,资源总是那么多,所以人们挖空心思要让计算机硬件发挥所有潜能。一个计算机中的资源主要是CPU,存储器,和IO设备。
当成熟的操作系统出现以后,硬件就逐渐被抽象成了一系列概念。在UNIX中,硬件设备的访问形式跟普通文件的访问形式一样。繁琐的硬件细节就交给了操作系统的硬件驱动程序来完成。
操作系统为什么要分段?分段 + 虚拟地址 解决了隔离地址空间+程序运行地址确定问题。
为什么分页?内存使用效率 + 额外的页保护。
线程

线程安全问题:
一条语句在汇编之后会变成多条指令,有可能在执行到某一条指令的时候,线程调度到另一个线程,比如读写数据时会导致数据的不同步。
单条指令是不会被调度离开的,称为原子操作。原子操作可以解决简单的不同步问题。因为一条指令不会再分。
尽管原子操作非常简单方便,但是只能使用在比较简单的场景下,在更为复杂的环境下,我们需要锁同步。
信号量:(每个线程进去的时候拿到锁,出去的时候解开锁)
- 二元信号量,只有零一两个状态,只允许一个线程拿到对数据的读写许可。
- 多元信号量,有n个状态,允许最多n个拿到锁。
互斥量:
和二元信号量相似,资源同时仅允许一个线程访问。和信号量不同的是同一个信号量可以由一个线程获取,由另一个线程释放,而互斥量由一个线程获取,也只能由这个线程释放。
临界区(Critical Section):
是比互斥量更加严格的同步手段。在术语中,把临界区的锁的获取称为进入临界区、而把锁的释放称为离开临界区。临界区和互斥量与信号量的区别在于,互斥量和信号量在系统的任何进程里都是可见的,也就是说,一个进程创建了一个互斥量或信号量,另一个进程试图去获取该锁是合法的。然而,临界区的作用范围仅限于本进程,其他的进程无法获取该锁。除此之外,临界区具有和互斥量相同的性质。
读写锁:
适用用更特殊的场景,读数据多,写数据少。读写锁有三种状态:自由态,共享态,独占态。
共享态:可以进行读操作
处于独占态:可以进行写操作
处于自由态可以转换为共享态或者独占态。
要想处于独占态,必须等待所有的线程的锁释放。
线程安全与过度优化
在很多年以前CPU就发展出了动态调度,可能会交换指令的顺序,编译器在进行优化的时候也有可能交换某两条不想干的指令顺序。这导致了在合理使用锁的情况下仍然存在线程不安全。
volatile 关键字:
- 阻止编译器为了提高速度将一个变量缓存到寄存去而不写回内存。
- 阻止编译器调整volatile变量的指令顺序。
volatile 可以解决编译器的过度优化:阻止编译器为了提高速度将一个变量缓存到寄存器中而不是写回,阻止编译器调整操作volatile变量的顺序。
但是它能解决CPU的动态调度吗?答案是不能。看看下面的代码(单例设计模式)

看着很不错,实际上存在一些问题。
pInst=new T :实际上执行了三个步骤,分配内存,构造,将指针交给pInst。第二步和第三步是可以颠倒的,这可能会导致,该实例正在构造,就已经将它的指针交给了另一个线程。
如何阻止CPU的换序动作,其实,CPU提供了barrier指令。一条barrier指令会阻止CPU将该指令之前的指令换到该指令之后,如图简介。


浙公网安备 33010602011771号