一、进程的引入:为什么需要进程?
问题背景:早期计算机一次只能运行一个程序。比如,你有一个计算程序和一个下载程序,你想边计算边下载,怎么办?
解决方案:引入进程的概念,让多个程序可以“同时”运行。实际上,在单核CPU上,是通过快速切换来实现的,但给用户的感觉是同时运行。
例子:你打开音乐播放器听歌,同时用浏览器上网。操作系统为每个程序创建了一个进程,并轮流执行它们(时间片轮转)。虽然CPU同一时刻只能执行一个进程,但由于切换很快,你感觉音乐和网页同时都在运行。
二、进程是什么?
具体例子:想象一个厨房(CPU)里有一个厨师(进程)。厨师要做一道菜(程序),他需要菜谱(代码)、食材(数据)、厨具(资源)。菜谱指明了步骤,但厨师在切菜的时候可能被叫去炒菜(切换),那么他必须记住切到哪了(保存现场),等他回来再接着切。
进程的定义:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位。它包括代码、数据、程序计数器、寄存器、堆栈等。
三、进程的状态与切换
面对的问题:进程在运行中,有时需要等待外部事件(比如等待用户输入、等待磁盘读取数据),如果让CPU一直等待,就浪费了。如何提高CPU利用率?
解决办法:让进程在等待时让出CPU,给其他就绪的进程使用。这就引入了进程状态。
例子:假设有两个进程A和B。A需要从磁盘读取数据,于是操作系统将A的状态改为“阻塞”,并让B运行。当磁盘数据准备好后,再将A的状态改为“就绪”,等待CPU调度。
典型的状态转换:
就绪:进程已准备好,只等CPU。
运行:进程正在CPU上执行。
阻塞:进程等待某个事件(如I/O完成)。
状态转换图:
新建 → 就绪 ↔ 运行 → 终止
↓
阻塞四、进程的创建与终止
创建:通过系统调用(如fork)创建新进程。
例子:在Linux终端中,你输入ls命令,shell进程会调用fork创建一个子进程,然后子进程调用exec执行ls程序。
终止:进程完成任务后,通过exit系统调用终止,释放资源(除了进程控制块PCB,最后会被父进程回收)。
五、进程间通信(IPC)
问题:进程之间是隔离的,不能直接访问对方的内存。但如果需要协作,比如一个进程生产数据,另一个进程消费数据,怎么办?
解决办法:操作系统提供进程间通信的机制。
具体例子:
管道(pipe):就像一条水管,一端写,另一端读。例如,在shell中执行ls | grep .txt,shell创建两个进程,一个执行ls,一个执行grep,并用管道连接起来。ls的输出直接作为grep的输入。
消息队列:进程A发送一个消息到队列,进程B从队列中读取。类似于发邮件。
共享内存:两个进程可以访问同一块内存区域。这是最快的IPC方式,但需要同步机制(如信号量)来避免冲突。
信号(signal):一个进程可以向另一个进程发送信号,比如按下Ctrl+C发送SIGINT信号终止进程。
六、进程同步
问题:当多个进程共享资源(比如共享内存、文件)时,可能会产生冲突。
例子:有两个进程A和B,它们都要往同一个文件里写数据。如果A写了一半,B也开始写,那么文件内容就会混乱。
解决办法:使用同步机制,确保同一时刻只有一个进程访问共享资源。
具体方法:
互斥锁(Mutex):进程在访问共享资源前加锁,访问完后解锁。其他进程必须等待锁释放。
信号量(Semaphore):一个计数器,用来控制多个进程对共享资源的访问。比如,只有3个资源,信号量初始值为3,每有一个进程占用一个资源,信号量减1,当减到0时,其他进程必须等待。
条件变量:用于等待某个条件成立。
七、进程调度
问题:有多个就绪进程,但CPU只有一个,先运行哪个?
调度算法例子:
先来先服务(FCFS):就像排队买票,先来的先服务。但如果有长进程先到,短进程就要等很久。
短作业优先(SJF):优先运行估计运行时间最短的进程。这可以减少平均等待时间,但长进程可能一直得不到运行(饥饿)。
时间片轮转(RR):每个进程分配一个时间片(比如100ms),时间片用完后,进程被剥夺CPU,放到就绪队列末尾。这样每个进程都能得到执行,但上下文切换开销较大。
多级反馈队列:结合了多种算法。有多个队列,每个队列优先级不同,时间片大小也不同。新进程进入最高优先级队列,如果时间片用完还没结束,就降到下一级队列。这样短进程可以很快完成,长进程也会得到执行。
八、上下文切换
问题:当CPU从一个进程切换到另一个进程时,需要保存当前进程的状态,以便下次恢复。
例子:进程A正在运行,时间片用完,操作系统要切换到进程B。那么,操作系统必须保存A的寄存器、程序计数器等状态到A的PCB中,然后从B的PCB中恢复B的状态,让B继续运行。
开销:上下文切换需要时间,如果切换太频繁,CPU的有效工作时间就会减少。
九、多线程与进程的关系
问题:进程的创建、切换开销较大,而且进程间通信复杂。如果我们要在一个程序内同时做多件事,比如一个文档编辑器,一边接收用户输入,一边自动保存,一边进行拼写检查,怎么办?
解决办法:引入线程。线程是进程内的执行单元,一个进程可以有多个线程,它们共享进程的资源(如内存、文件),但各自有独立的栈和寄存器。
例子:一个Web服务器,每来一个客户端请求,就创建一个线程来处理。这样,多个请求可以同时被处理(在多核CPU上真正并行,单核上也是并发)。因为线程共享进程的内存,所以创建线程和线程间通信的开销比进程小。
十、总结
进程是操作系统资源分配的基本单位,它使得多个程序可以并发执行,提高了系统资源的利用率。但进程的创建、切换、通信都有开销,因此后来又引入了线程。理解进程的关键是理解它的状态转换、调度、同步和通信机制,这些都是为了解决多任务环境下的资源竞争和协作问题。
解决方案:在一个进程内部创建多个线程,每个线程执行不同的任务,共享同一份数据。
具体实现:
线程是进程内的执行单元,一个进程可以有多个线程
同一进程的线程共享内存空间(代码段、数据段、打开的文件等)
每个线程有独立的栈和程序计数器
例子:Word进程中的多线程
Word进程:
线程1:处理用户输入(键盘、鼠标)
线程2:定时自动保存
线程3:拼写和语法检查
线程4:打印文档二、线程是什么?—— 进程的“分身”
比喻:餐厅厨房里的多个厨师
整个厨房 = 进程(拥有食材、厨具等资源)
每个厨师 = 线程(共享厨房资源,各自做一道菜)
厨师间的协作 = 线程通信(直接说话,因为共享空间)
具体例子:Web服务器处理多个客户端请求
单进程多线程模型:
主线程:监听端口,接受连接
工作线程1:处理客户端A的请求
工作线程2:处理客户端B的请求
工作线程3:处理客户端C的请求
所有线程共享服务器资源(如缓存、数据库连接池)
线程共享的“公共空间” vs 私有的“个人空间”
进程内的线程共享资源指的是:进程的地址空间中的大部分区域,包括代码段、数据段、堆、以及打开的文件描述符等。而每个线程拥有自己独立的栈空间(用于保存局部变量和函数调用信息)以及线程私有的上下文(如寄存器状态、程序计数器等)。
具体来说:
代码段:所有线程执行相同的代码,它们共享进程的代码段(即程序的可执行代码)。
数据段:包括全局变量和静态变量,所有线程都可以访问和修改这些变量,因此需要同步机制来避免竞态条件。
堆:动态分配的内存(如malloc/new分配的内存)是共享的,所有线程都可以访问堆中的同一块内存。
文件描述符:进程打开的文件、网络套接字等,所有线程共享这些文件描述符,因此一个线程打开的文件,另一个线程也可以读写。
信号处理程序:进程设置的信号处理函数是共享的,但每个线程可以有自己独立的信号屏蔽字。
当前工作目录:进程的当前工作目录是共享的,一个线程改变工作目录会影响其他线程。
用户ID和组ID:进程的用户ID和组ID是共享的。
每个线程私有的资源包括:
线程ID:每个线程有唯一的ID。
寄存器状态:包括程序计数器、栈指针等,每个线程有自己的寄存器状态。
栈:每个线程有自己独立的栈,用于存储局部变量、函数调用链等。线程之间不能直接访问其他线程的栈。
信号屏蔽字:每个线程可以独立设置阻塞或解除阻塞某些信号。
线程私有数据:有些系统支持线程私有数据(Thread Local Storage, TLS),即每个线程可以拥有某些数据的独立副本。
理解线程共享哪些资源非常重要,因为这直接影响到多线程程序的设计和同步机制的使用。例如,共享的数据需要加锁保护,而线程私有的数据则不需要。
三、线程 vs 进程 —— 详细对比
四、线程同步——解决“共享数据混乱”问题
问题场景:两个线程同时操作同一个银行账户,一个取钱,一个存钱,如果没有同步,账户余额可能会出错。
互斥锁(Mutex)—— 卫生间一次只能一个人用
// 银行账户例子 int balance = 1000; // 共享变量 pthread_mutex_t lock; // 线程A:取钱100 pthread_mutex_lock(&lock); int temp = balance; temp -= 100; balance = temp; pthread_mutex_unlock(&lock); // 线程B:存钱200 pthread_mutex_lock(&lock); int temp = balance; temp += 200; balance = temp; pthread_mutex_unlock(&lock);条件变量(Condition Variable)—— 排队等空位
// 生产者-消费者问题(线程版) queue buffer; // 共享缓冲区 pthread_mutex_t mutex; pthread_cond_t cond_full; // 缓冲区满的条件变量 pthread_cond_t cond_empty; // 缓冲区空的条件变量 // 生产者线程 pthread_mutex_lock(&mutex); while (buffer.is_full()) { pthread_cond_wait(&cond_full, &mutex); // 等待缓冲区不满 } buffer.push(item); pthread_cond_signal(&cond_empty); // 通知消费者缓冲区不空了 pthread_mutex_unlock(&mutex); // 消费者线程 pthread_mutex_lock(&mutex); while (buffer.is_empty()) { pthread_cond_wait(&cond_empty, &mutex); // 等待缓冲区不空 } item = buffer.pop(); pthread_cond_signal(&cond_full); // 通知生产者缓冲区不满了 pthread_mutex_unlock(&mutex);读写锁(Read-Write Lock)—— 图书馆规则
场景:多个线程同时读一个配置,偶尔有线程写配置 规则: 1. 可以多个线程同时读 2. 写的时候不能有任何其他读或写 实现: 读写锁有三种状态:读模式加锁、写模式加锁、不加锁 写模式加锁时,其他线程的读或写都会被阻塞 读模式加锁时,其他线程的写会被阻塞,但读可以继续五、线程池——避免“频繁创建销毁线程”的开销
问题场景:Web服务器为每个请求创建一个新线程,请求处理完后销毁。当请求很多时,创建和销毁线程的开销很大。
解决方案:预先创建一组线程,重复使用。
线程池组成:
1. 任务队列:存放待处理的任务
2. 工作线程:从任务队列取任务执行
3. 管理器:创建、销毁线程,调整线程数量例子:Java中的线程池
ExecutorService pool = Executors.newFixedThreadPool(10); // 创建10个线程的池
// 提交100个任务
for (int i = 0; i < 100; i++) {
pool.submit(new Task(i));
}
pool.shutdown(); // 关闭线程池优势:
降低资源消耗:重复利用已创建的线程
提高响应速度:任务到达时,线程已存在,无需等待创建
提高可管理性:可以统一管理线程
调试多线程问题
常见问题与排查工具
问题1:竞态条件
症状:结果不确定,每次运行可能不同
工具:ThreadSanitizer (TSan)、Helgrind问题2:死锁
症状:程序卡住,不响应
工具:jstack (Java)、pstack、gdb
例子:jstack <pid> | grep -A 10 deadlock问题3:活锁
症状:线程忙碌但无进展
例子:两个线程互相"礼让",都不断重试失败问题4:资源泄漏
症状:内存或线程数持续增长
工具:jvisualvm、top -H、pmap总结:线程使用指南
黄金法则:
能不共享就不共享:减少共享数据,避免同步问题
能无锁就无锁:使用原子变量、不可变对象
必须同步时,粒度尽量小:减少锁竞争
优先使用高级工具:线程池、并发集合、同步工具类
线程是强大的并发工具,但"能力越大,责任越大"。理解其原理,遵循最佳实践,才能写出正确高效的多线程程序。
现代软件中的线程使用
案例1:Android应用的主线程与工作线程
// Android中,UI操作必须在主线程(UI线程)
class MainActivity : AppCompatActivity() {
fun loadUserData(userId: String) {
// 在后台线程执行耗时操作
thread {
val userData = apiService.getUserData(userId) // 网络请求(可能耗时)
// 回到主线程更新UI
runOnUiThread {
textView.text = userData.name // UI操作必须在主线程
progressBar.visibility = View.GONE
}
}
}
}规则:主线程只处理UI更新和用户交互,耗时操作(网络、数据库、计算)必须放在工作线程。
案例2:Redis的单线程模型(为什么快?)
Redis核心是单线程的,但性能极高,原因:
1. 纯内存操作:无磁盘I/O阻塞
2. 非阻塞I/O:使用epoll多路复用
3. 避免锁竞争:单线程不需要同步
4. 数据结构优化:高效的数据结构
适用场景:高并发、数据量小、读写频繁
不适用场景:复杂计算、大数据处理案例3:Nginx的多进程+多线程混合模型
Nginx架构:
┌─ Master进程(管理)─────────────────────┐
│ ├─ Worker进程1(独立) │
│ │ ├─ 线程1:处理连接A │
│ │ ├─ 线程2:处理连接B │
│ │ └─ 线程池:处理磁盘I/O │
│ ├─ Worker进程2(独立) │
│ └─ Worker进程N(独立) │
设计优点:
1. 进程隔离:一个Worker崩溃不影响其他
2. 线程共享:同一Worker内线程共享缓存
3. 充分利用多核:每个Worker绑定一个CPU核心虚拟存储管理系统的基础是程序的局部性理论,这个理论的基本含义是指程序执行时往往不会均匀地访问主存储器单元。根据这个理论,Denning 提出了工作集理论。工作集是进程运行时被频繁访问的页面集合。在进程运行时,如果它的工作集页面都在主存储器(内存)内,能够使该进程有效地运行,否则会出现频繁的页面调入/调出现象。
时间局部性:如果程序中的某条指令或数据被访问,那么不久之后它很可能再次被访问。典型例子包括循环、重复调用的函数等。
空间局部性:如果程序访问了某个存储单元,那么其附近的存储单元也可能很快被访问。典型例子包括顺序执行的指令、数组的连续访问等。
基于局部性原理,程序运行时只需将当前使用的部分页面装入内存,而非全部程序,从而实现虚拟存储器的高效管理。
进程通信PV操作与信号量核心知识点总结
初值=0 意味着初始状态没有任何可用资源,事件未发生,必须等待通知
P操作 就像"瞅一眼桌子",发现没饭就睡觉(进程阻塞)
V操作 就像"外卖员按门铃",把你叫醒继续执行(进程唤醒)
原子性保证:
// 假设S=1,两辆车同时P操作
车A: 读取S=1 → S--=0 → 进入停车场
车B: 读取S=1 → S--=0 → 也进入停车场 ❌
// 结果:一个车位停两辆车!碰撞!正确顺序:先P资源信号量,再P互斥信号量
原理:如果先拿锁,当资源耗尽时,其他进程无法进入临界区释放资源,形成死锁
初值=1:商场厕所(互斥访问)场景:只有一间厕所,拉屎要排队
数据库行锁:1个线程修改,其他线程阻塞
打印机互斥:1个人打印,其他人排队
技术映射:保护临界区,一次只允许一个进程访问
初值=N:停车场(资源池)场景:10个车位,先到先得
图书馆座位:初值=500个座位
数据库连接池:初值=5个连接
线程池任务队列:初值=100个任务容量
资源申请顺序不一致
Process1 { P(A); P(B); ... V(B); V(A); } // 先A后B
Process2 { P(B); P(A); ... V(A); V(B); } // ❌ 先B后A
// 结果:Process1占A等B,Process2占B等A → **死锁!**正确:全局统一起始顺序
Process1 { P(A); P(B); ... }
Process2 { P(A); P(B); ... } // ✅ 顺序一致信号量负值理解错误
题目:3个进程共享1台打印机,信号量初值=1,当前值=-2,问什么情况?
❌ 错误答案:有2台打印机空闲(完全错误!)
✅ 正确答案:2个进程在等待打印机(|-2|=2)
编写PV操作(黄金顺序)
Process {
准备工作;
P(资源信号量); // 1️⃣ 先检查资源是否可用
P(互斥信号量); // 2️⃣ 再进入临界区(上锁)
操作共享资源; // 3️⃣ 临界区代码
V(互斥信号量); // 4️⃣ 先出临界区(解锁)
V(通知信号量); // 5️⃣ 再通知其他进程
后续操作;
}📌 初值选择:互斥用1,同步用0,资源就是N
📌 PV本质:P是申请,不够就等;V是释放,唤醒他人
📌 操作顺序:先资源后锁,先解锁后通知
📌 信号量值:正数可用,零刚好用,负数等几
📌 死锁避免:统一申请顺序,限制最大并发
进程调度知识点
进程调度本质
核心问题:CPU只有一个,成百上千个进程要运行,按什么顺序执行?
生活类比:餐厅只有一个厨师,怎么安排炒菜顺序才能让顾客最满意?
2.短作业优先(SJF)
原理:执行时间短的优先,分抢占/非抢占
问题:长作业可能饿死
生活场景:快餐店小份餐优先出餐,大胃王套餐最后做
3.优先级调度
原理:按优先级执行,分静态/动态优先级
生活场景:医院急诊分级(濒危>危重>急症>非急症)
数字越小优先级越高
进程 | 到达 | 执行 | 优先级
P1 | 0ms | 8ms | 3
P2 | 1ms | 4ms | 1 ← 最高
P3 | 2ms | 9ms | 4
抢占式执行
0----1----5----14
|P1 |P2 |P1 |P34.时间片轮转(RR)
原理:每个人轮流吃一口,看起来都很忙
生活场景:自助餐每人限时5分钟,轮流用餐
时间片=5ms执行示例:
进程 | 到达 | 执行
P1 | 0ms | 13ms
P2 | 1ms | 4ms
P3 | 2ms | 6ms
0---5---9---15---18
|P1 |P2 |P3 |P15.多级队列调度
原理:按性质分多个队列,不同队列不同策略
生活场景:火车站分窗口:
军人/急客窗口(优先级+RR)
普通窗口(FCFS)
团购窗口(批处理)
前台队列(交互式):RR调度,时间片=8ms
后台队列(批处理):FCFS,时间片=32ms
系统队列(内核):优先级最高
队列规则:
进程固定属于某队列
队列间按优先级调度
队列内用各自算法
6.多级反馈队列(MFQ)
原理:RR + 优先级 + 队列升级(Linux实际使用)
生活场景:餐厅会员体系:
新客→普通座(Q0)
超时用餐→升包厢(Q1)
长期用餐→升VIP(Q2)
Q0(优先级最高):RR时间片=8ms
Q1:RR时间片=16ms
Q2(最低):FCFS时间片=32ms
核心规则:
新进程进入Q0
用完时间片未结束 → 降级到下一队列
等待过久未执行 → 提升到上级队列
高优先级队列非空时,不执行低优先级
P1: 到达=0, 执行=7
P2: 到达=2, 执行=4
P3: 到达=4, 执行=1
0-3: P1 → 3-6: P2 → 6-7: P3 → 7-10: P2 → 10-14: P1
进程 | 到达 | 服务 | 完成 | 周转 | 带权 | 等待
P1 | 0 | 7 | 14 | 14 | 2.0 | 0+(10-3)=7
P2 | 2 | 4 | 10 | 8 | 2.0 | (3-2)+(7-6)=2
P3 | 4 | 1 | 7 | 3 | 3.0 | 6-4=2
计算平均值
平均等待 = (7+2+2)/3 = 3.67ms
平均周转 = (14+8+3)/3 = 8.33ms
平均带权 = (2+2+3)/3 = 2.33陷阱1:抢占式vs非抢占式混淆
错误:题目说"抢占式SJF",却按非抢占计算
正确:SRTF要在每个新进程到达时重新选择最短剩余时间
P1(执行10ms)运行中,P2(执行1ms)到达 → 立即抢占P1执行P2 ✅陷阱2:时间片轮转切换开销忽略
时间片=5ms,切换开销=1ms
P1(0-4)运行 → P2(5-9)运行 → 实际耗时不是5ms而是6ms!陷阱3:带权周转时间算错
执行时间=3ms,周转=24ms
带权 = 24/3 = 8 ✅
不是 3/24 = 0.125 ❌陷阱4:响应时间理解错误
错误:认为响应时间=结束时间
正确:响应时间=首次运行时间-到达时间(第一次被调度)
陷阱5:多级队列饥饿问题
低优先级队列进程永远不执行 → 解决方案:优先级老化(等待越久优先级越高)
陷阱6:护航效应识别
题目:长作业+多个短作业用FCFS
后果:短作业等待时间巨长 → 解决方案:用SJF或RR
现实操作系统应用
Linux CFS调度器
核心理念:完全公平调度(Completely Fair Scheduler)
关键技术:红黑树管理进程,按虚拟运行时间vruntime排序
计算公式:vruntime = 实际时间 × (NICE_0_LOAD / 权重)
调度周期:所有进程至少运行一次的时间
生活类比:班级轮流发言,说话时间短的人优先
Windows调度器
32级优先级:0-31,0为最低
动态调整:前台窗口线程+2,I/O操作后+1
实时优先级:16-31级(需管理员权限
实时系统调度
硬实时 必须准时完成 最早截止时间优先(EDF)
软实时 偶尔超时允许 速率单调调度(RMS)
案例:导弹控制→EDF算法;视频播放器→RMS算法
进程调度就是CPU的"交通警察":
FCFS是排队,SJF是绿色通道,RR是轮流执政,
多级反馈是智能交通系统,最终目标——让所有"车辆"都满意!
在进程资源图中,箭头是描述进程和资源之间关系的关键元素,它清晰地表达了资源的申请和分配状态。简单来说,箭头从谁指向谁,就表示“谁需要谁”或“谁等待谁”。
- 申请边(Request Edge)
方向:从 进程 指向 资源类(方框)。
涵义:表示该进程正在申请(等待)该资源类中的一个实例,但尚未得到。
状态:进程因此被阻塞,处于等待状态。
口语化理解:“进程想要一个这种资源,但还没拿到。” - 分配边(Assignment Edge) / 持有边(Allocation Edge)
方向:从 资源类中的某个具体实例(方框中的圆点)指向 进程。
涵义:表示该资源类中的一个具体实例已经分配给了这个进程,由该进程持有。
状态:进程正占用着此资源。
口语化理解:“这个资源实例已经在进程手里了。
如果资源有空闲,请求会被瞬间满足,申请边在快照中就不会出现(它会被立即转换为一条分配边)。因此,在静态资源图分析中,默认图中出现的每一条申请边都对应一个被阻塞的进程。
假设资源有三个能分配的资源,但是已经有三个分配箭头了,这时还有一个申请边,那就是阻塞
关于b图:R2的3个资源已经占用了2个,当进程P1和P3请求占用R2的时候,无论分配给哪一方都可以使两个进程都满足所需的资源,从而可以化简,P2也可得所需的R1资源。因此P1和P3是非阻塞节点。
疑问:“申请边存在不是意味着阻塞吗?”
是的,在静态快照中,画出的申请边意味着该进程在快照时刻因该请求而处于阻塞状态。
但是,在动态化简分析中,我们关心的是这种阻塞是否是“永久性的”。如果系统当前有足够的空闲资源能立即(或在其他进程释放资源后)满足该请求,那么这个阻塞就是暂时的、可解除的。这样的进程在化简中就被归为“非阻塞节点”。
“非阻塞节点”的真实含义:更准确的说法是“可立即满足或可被化简的节点”。它不是指进程在快照时刻未阻塞,而是指从当前系统状态出发,该进程的请求能够在不引起死锁的情况下被满足。
动态分区内存管理中的空闲分区合并(或合并自由块)
在动态分区分配方式中,当进程运行结束释放内存时,系统会将其占用的分区回收,并检查该分区是否与相邻的空闲分区相邻(地址连续)。如果回收区与上方和下方的空闲区都相邻,则系统会将这三个分区合并为一个更大的空闲分区,从而导致空闲区总数减少1。此过程旨在减少外部碎片,提高内存利用率。
要保证系统中 6 个并发进程(每个进程都需要 2 个互斥资源 R)不发生死锁,资源 R 的最少数目为 7。
推导过程:
在最坏情况下,每个进程都已获得 1 个资源,同时还需要 1 个资源才能继续执行。此时如果系统中只剩 1 个可用资源,则可分配给任意一个进程,使其满足全部资源需求并运行完毕,之后释放的资源又能供其他进程使用,从而避免死锁。
因此,最少资源数 = 进程数 × (每个进程所需资源数 - 1) + 1 = 6 × (2 - 1) + 1 = 7。



评论 (0)