温故而知新

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将该指令之前的指令换到该指令之后,如图简介。

 

posted @ 2022-12-16 15:24  stu--wy  阅读(27)  评论(0)    收藏  举报