原稿:语雀 · 操作系统 · 原目录:操作系统
进程与线程
用课本的话回答“进程是资源分配的最小单位,线程是CPU调度的最小单位”
怎么理解:
- 进程:当执行一段程序时,会创建一个进程去执行代码,同时会为这个进程分配内存空间。该应用的状态都会保存在该内存空间中,当应用关闭时,该内存空间就会被回收。进程可以启动更多进程去执行任务。由于每个进程间的的内存空间是独立的,不同进程间的数据传输需要接触进程通信管道(IPC)来进行。很多应用都是多进程的结构,避免一个进程卡死后整个程序崩溃。
- 线程:进程可以将一个任务分成更多细小的任务,然后通过创建多个线程,并行执行不同的任务,同一进程下的线程可以直接通信、共享数据。多个线程按一定方式占据 CPU 的时间片以执行任务,是调度过程的参与者
进程间通信方式
进程间通信即 IPC,其实现由多种方法
管道 pipe
管道是一种适合于父子进程间通信的方式,可视为一种文件,但只存在于内存中。父进程在fork 子进程之前会创建 IPC 管道并监听它,当真正创建出子进程后,通过环境变量告诉子进程 IPC 管道的文件描述符,子进程启动过程中会根据文件描述符连接已存在的管道,完成父子进程的连接。

管道可以连接两个地址空间进行数据传输,每个管道会创建两个文件描述符,fd
打开表示读,fd
打开表示写,若要数据流从父进程流向子进程,则关闭父进程的读端(fd
)与子进程的写端(fd
)

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

消息队列
消息队列,是消息的链表,存放在内核中。一个消息队列由一个标识符(即队列 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 向下执行
死锁避免
资源分配拒绝算法(银行家算法),已知当前可分配资源,以及各个进程执行需要的初始最大资源和已分配的资源,此时选择一个可用资源能支持执行完毕的进程,执行完毕;回收所有资源作为当前可用资源,重复,如果出现当前可用资源不能支持任何进程的执行,则称为不安全状态——需要某个进程提前释放资源避免死锁
死锁检测
使用银行家算法检测当前是否处于安全状态,如果非安全状态,则回滚直到安全状态,或者直接结束死锁进程
死锁与银行家算法细节见下: