进程与同步
一句话模型
进程拥有资源、线程执行代码;多个执行流共享资源时需要同步和互斥来约束执行顺序,若资源分配形成循环等待,就会进入死锁,需要通过预防、避免或检测解除来处理。
三层递进
- 简单:区分进程、线程、资源和调度状态,建立并发执行的参与者模型。
- 中等:从临界区出发,连接互斥锁、信号量、条件变量和生产者/消费者流程。
- 复杂:把资源分配图、死锁四个必要条件、银行家算法和检测解除策略叠加起来。
简单图谱:进程与线程
进程主要解决“资源归谁所有”,线程主要解决“由谁执行代码”。PCB 保存进程级资源和状态,线程控制块保存各执行流的寄存器、PC、栈和调度状态。
# 进程与同步
## 并发执行主体
- 程序:静态指令和数据
- 进程:程序的一次执行,资源分配基本单位
- 线程:进程内的执行流,调度基本单位
- PCB / TCB:保存管理信息和处理机现场
## 共享状态
- 进程地址空间与打开文件
- 线程共享进程资源
- 临界资源与临界区
## 风险
- 竞态条件:结果依赖执行交错
- 不一致:复合操作被切换打断
- 资源等待:可能导致死锁
中等图谱:同步与互斥
主干问题是:多个执行流同时访问共享资源时,如何保证结果正确,并在需要时建立先后顺序?
一次临界区访问可以这样追踪:线程先执行非临界区;进入临界区前获取锁或执行 P/wait;获得许可后访问共享资源;离开临界区时解锁或执行 V/signal,唤醒等待者。
- 互斥约束“同一时刻最多一个执行流进入临界区”。
- 同步约束“多个执行流之间满足某种先后关系”,例如消费者必须等待缓冲区非空。
- 信号量既能实现互斥锁,也能用计数表示可用资源数量。
- 条件变量通常与互斥锁配合,等待条件不满足时释放锁并阻塞。
复杂图谱:资源等待与死锁
死锁不是普通的阻塞,而是多个进程互相等待、任何一个都无法继续推进的闭环。复杂图把同步原语造成的资源占用,连接到死锁的检测和处理策略。
阅读时沿三条路径追踪:
- 正常路径:申请资源 → 获得资源 → 执行 → 释放资源。
- 死锁形成路径:互斥 + 占有并等待 + 非抢占 + 循环等待同时成立。
- 处理路径:预防破坏必要条件;避免在分配前做安全性检查;检测发现环后解除或回收资源。
子模块图谱:同步原语
同步原语的差异可以归结为“保护什么状态、阻塞在哪里、由谁唤醒”。互斥锁保护临界区,信号量管理许可或资源计数,条件变量等待谓词成立。
子模块图谱:死锁处理
死锁处理策略对应不同的系统取舍:预防限制并发资源申请,避免需要知道最大需求并运行银行家算法,检测解除则允许风险发生后再恢复。
关键问题
进程和线程为什么要分成两个抽象?
进程隔离地址空间和系统资源,线程共享进程资源并承担执行与调度;这样既能保护资源,又能在同一进程内低成本并发。
同步和互斥有什么区别?
互斥是排他访问,回答“谁能进入临界区”;同步是执行顺序约束,回答“谁应该先做、谁需要等待”。一个同步问题可以同时包含互斥和条件等待。
为什么加锁后仍然可能死锁?
锁只保证单个临界区的互斥,不自动保证多个锁的申请顺序。如果线程 A 持有锁 1 等锁 2,线程 B 持有锁 2 等锁 1,就形成循环等待。
银行家算法检查的是什么?
它检查的是“试探性分配后是否仍存在安全序列”,不是判断当前是否已经死锁。安全状态表示存在一个顺序,使所有进程都能获得剩余资源并完成。
旁路清单
- 进程状态:创建、就绪、运行、阻塞、终止与状态转换
- 线程实现:用户级线程、内核级线程和多线程模型
- 同步实现:禁用中断、原子指令、自旋锁、互斥锁、信号量
- 经典问题:生产者/消费者、读者/写者、哲学家进餐
- 死锁模型:资源分配图、可达性、安全序列和安全性检查
- 死锁解除:抢占资源、终止进程、回滚和重新分配