原稿:语雀 · 操作系统 · 原目录:操作系统

进程与线程

用课本的话回答“进程是资源分配的最小单位,线程是CPU调度的最小单位”

怎么理解:

  • 进程:当执行一段程序时,会创建一个进程去执行代码,同时会为这个进程分配内存空间。该应用的状态都会保存在该内存空间中,当应用关闭时,该内存空间就会被回收。进程可以启动更多进程去执行任务。由于每个进程间的的内存空间是独立的,不同进程间的数据传输需要接触进程通信管道(IPC)来进行。很多应用都是多进程的结构,避免一个进程卡死后整个程序崩溃。
  • 线程:进程可以将一个任务分成更多细小的任务,然后通过创建多个线程,并行执行不同的任务,同一进程下的线程可以直接通信、共享数据。多个线程按一定方式占据 CPU 的时间片以执行任务,是调度过程的参与者

进程间通信方式

进程间通信即 IPC,其实现由多种方法

管道 pipe

管道是一种适合于父子进程间通信的方式,可视为一种文件,但只存在于内存中。父进程在fork 子进程之前会创建 IPC 管道并监听它,当真正创建出子进程后,通过环境变量告诉子进程 IPC 管道的文件描述符,子进程启动过程中会根据文件描述符连接已存在的管道,完成父子进程的连接。

image.png

管道可以连接两个地址空间进行数据传输,每个管道会创建两个文件描述符,fd

00

打开表示读,fd

11

打开表示写,若要数据流从父进程流向子进程,则关闭父进程的读端(fd

00

)与子进程的写端(fd

11

323808-20160311094030069-935122142.png

FIFO

FIFO 是一种先进先出的队列。它类似于一个管道,只允许数据的单向流动。每个 FIFO 都有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中,允许不相关的进程访问同一个 FIFO。因此也成为命名管道。使用 FIFO 通信时,在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。

FIFO.png

消息队列

消息队列,是消息的链表,存放在内核中。一个消息队列由一个标识符(即队列 ID)来标识。不同进程将格式化的数据流以消息形式发送给任意进程,该数据会被消息队列接收。对消息队列具有操作权限的进程都可以使用 msgget 完成对消息队列的操作控制。通过使用消息类型,进程可以按任何顺序读消息,或为消息安排优先级顺序

信号量 + 共享内存

共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。因为多个进程可以同时操作,所以需要借助信号量进行同步。以简单的二元信号量为例,seg 只能为 0 或 1,当进程 A 读取内存时,使信号量减一,seg 变成 0,进程 B 想要访问内存时就会被堵塞,当 A 读取结束,信号量加一,seg 变回1,此时进程 B 恢复执行,开始读取数据。


死锁

死锁是多个进程/线程在运行过程中因争夺资源而造成的一种僵局:线程 A 拥有资源 a,此时需要获取资源 b;线程 B 拥有资源 b,需要获取资源 a,此时 A 锁住了 a,B 锁住了 b,二者共同等待对方释放资源,此时发生死锁

死锁预防

  • 一次性分配:运行进程执行前一次性请求所有资源
  • 允许资源剥夺:为任务分配优先级,高优先级进程可抢占低优先级进程资源,需要保存低优先级进程的状态,方便恢复
  • 定义资源线性顺序:已知 Ri<Rj,进程 A 持有 Ri,要请求 Rj,B 持有 Ri,要请求 Rj,由于资源顺序不可逆向,则 B 的请求被拒绝,此时剥夺 B 占有资源,A 向下执行

死锁避免

资源分配拒绝算法(银行家算法),已知当前可分配资源,以及各个进程执行需要的初始最大资源和已分配的资源,此时选择一个可用资源能支持执行完毕的进程,执行完毕;回收所有资源作为当前可用资源,重复,如果出现当前可用资源不能支持任何进程的执行,则称为不安全状态——需要某个进程提前释放资源避免死锁

死锁检测

使用银行家算法检测当前是否处于安全状态,如果非安全状态,则回滚直到安全状态,或者直接结束死锁进程

死锁与银行家算法细节见下:

Ch6 并发: 死锁和饥饿 1. 死锁原理死锁一组相互竞争系统资源或通信的进程发生"永久阻塞"系统资源可重用 reusable一次仅供一个进程安全使用且不会因为使用而耗尽,数量有限死锁情况:两个进程分别占有一个资源,但都请求对方占有的资源,产生死锁可消耗consumable可被生产/消耗的资源,数量通常无限 死锁情况:参… 劝退宝典 🤦‍♀️