1. 计算机组成
1.1 CPU的并发控制(CPU级别)
cpu的并发控制一般指的就是在多核心cpu环境下, 对三级缓存,内存中数据的一致性保证, 以及对多核cpu流程的控制等
1.1.1 关中断
中断是指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。即在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,此时,CPU暂时中止程序的执行转而处理这个新的情况的过程就叫做中断。
而关中断是指在此中断处理完成前,不处理其它中断.
1.1.2 缓存一致性协议
在CPU和内存的资源交换中,CPU常常需要等待内存,而浪费了大量的计算能力。三级缓存的出现正是为了弥补内存的慢和CPU的快而诞生的产物。
缓存的加入是为了解决CPU运算能力和内存读写能力的不匹配问题,简单来说就是为了提升资源利用率。那么在多CPU多核心下,每个核心都会有一个一级缓存或者二级缓存,也就是说一二级缓存是核心独占的(类似JMM模型,线程的工作内存是线程独占的,主内存是共享的)而三级缓存和主内存是共享的,这样就将导致CPU缓存一致性问题。为了解决这种不一致,大名鼎鼎的MESI协议随之而来。
MESI:Modified(修改),Exclusive(独占),Shared(共享),Invalid(无效)由以上数据的四种状态的首字母而来。
在缓存中数据的存储单元是缓存行(Cache line),主流的CPU缓存行都是64个字节。缓存行的四种状态由两个字节标识。
M(Modified): 这行数据有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。
E(Exclusive): 这行数据有效,数据和内存中的数据一致,数据只存在于本 Cache 中。
S(Shared): 这行数据有效,数据和内存中的数据一致,数据存在于很多 Cache 中。
I(Invalid): 这行数据无效。
MESI带来的相关问题, 比如进行了指令重排后的可见性问题, 是使用内存屏障解决的, 更多详情参考: https://www.cnblogs.com/hello-shf/p/12091591.html
1.1.3 系统屏障(内存屏障)
内存屏障,也称内存栅栏,内存栅障,屏障指令等, 是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。
指令1 :sfence 写屏障 在sfence 指令前的写操作 必须在sfence 指令的写操作前完成。
指令2: lfence 读屏障 在lfence指令前的读操作 必须在lfence指令的读操作前完成。
指令3:mfence 读写屏障 在mfence 指令的读写操作 必须在mfence指令的读写操作前完成。
内存屏障存在的意义就是为了解决程序在运行过程中出现的内存乱序访问问题,内存乱序访问行为出现的理由是为了提高程序运行时的性能(比如指令重排)。
1.1.4 总线锁
一个处理器在总线上输出LOCK#信号,使得其他处理器对内存的操作请求都会被阻塞,该处理器独占共享内存。
2. 操作系统概述
2.1 操作系统概述: 用户空间和内核空间
一般的操作系统对执行权限进行分级,分别为用户态和内核态。用户态相较于内核态有较低的执行权限,很多操作是不被操作系统允许的。
内核态相当于一个介于硬件与应用之间的层,内核有ring 0的权限,可以执行任何cpu指令,也可以引用任何内存地址,包括外围设备, 例如硬盘, 网卡,权限等级最高。
用户态则权利有限,例如在内存分配中,有一部分内存是仅为内核态使用的,用户态code则不允许访问那些内存地址,每个进程只允许访问自己申请到的内存。而且不允许访问外围设备。另外在执行cpu指令的时候也可以被高优先级抢占。
大多数时间各类程序都是执行在用户态下。
2.2 操作系统概述: 时钟与中断
Linux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。
“时钟中断”是特别重要的一个中断,因为整个操作系统的活动都受到它的激励。系统利用时钟中断维持系统时间、促使环境的切换,以保证所有进程共享CPU;利用时钟中断进行记帐、监督系统工作以及确定未来的调度优先级等工作。可以说,“时钟中断”是整个操作系统的脉搏。时钟中断属于硬中断
中断机制分为两种:
外中断: 中断信号来源于→外部设备
内中断: 中断信号来源于→当前指令
内中断情况一 陷阱/陷入: 由应用程序主动引发
内中断情况二 故障: 由错误条件引发
内中断情况三 终止: 由致命错误引发
2.3 操作系统概述: 中断的处理过程
2.4 操作系统概述: 原语
原语是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中断(具有原子性)。在操作系统中,某些被进程调用的操作,如队列操作、对信号量的操作、检查启动外设操作等,一旦开始执行,就不能被中断,否则就会出现操作错误,造成系统混乱。所以,这些操作都要用原语来实现
2.5 操作系统概述: 系统调用表
系统调用表是Linux内核中的一个数据结构,在系统调用过程中,最终会在内核态中查找系统调用表,借此定位相应系统服务函数的地址。
Linux的系统调用是预先定义好的,每一个系统调用都有一个编号,在内核中有一个对应的服务函数。在Linux的发展过程中,它提供的系统调用也在不断变化,从Linux.0的135个系统调用发展到Linux3.0的347个系统调用,系统调用表也不断扩大。
系统调用表及与其相关的若干机制并非是在CPU中实现的,而是由操作系统提供的。在Linux操作系统中,应用程序通常使用SYSENTER、SYSCALL或INT0x80进入系统调用,内核态程序system_call(位置linux4.20版本)根据EAX中的系统调用号查找系统调用表,获得调用号对应的服务函数,调用该函数完成系统调用的服务处理工作
2.6 进程
进程是一个具有一定独立功能的程序, 关于某个数据集合的一次运行活动, 是系统进行资源分配和调度的一个独立单位
- 进程是程序的一个实例
- 进程是一个程序及其数据在计算机上顺序执行时所发生的活动
- 进程是程序在一个数据集合上运行的过程
进程的结构:
- 控制块(PCB) Process Control Block 进程唯一标识
- 数据段 存放原始数据 中间数据等
- 程序段 存放在文本区域, 可被多个进程共享(多次运行同一个应用程序)
进程的特征:
- 动态性: 由创建而生, 由撤销而亡
- 并发性: 多个进程同时运行
- 独立性: 独立资源分配
- 异步性: 相互独立, 互不干扰
2.7 线程
是一系列活动按事先设定好的顺序依次执行的过程, 是一系列指令的集合
是一条执行路径, 不能单独存在, 必须包含在进程中
线程是OS中运算调度的最小单位
2.8 进程与线程
- 进程是资源分配的最小单元, 线程是运算调度的最小单元
- 通常来讲, 创建一个进程就是在内存中开辟一块空间, 这块空间就属于创建的这个进程
- 线程是要依附于进程之下的, 线程是一条单一的顺序控制流程, 线程不拥有资源, 只有一些执行必要的数据结构, 一般线程执行所需要的资源都在进程之中
2.9 进程间通信 (Inter-Process Communication)
消息队列
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
特点
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
- 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
信号量
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
特点
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
- 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
- 支持信号量组。
共享内存
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
特点
- 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
- 因为多个进程可以同时操作,所以需要进行同步。
- 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。