进程、线程、管程
进程
程序:指令序列
进程是程序的一次执行过程(动态) ;是进程实体的运行过程
进程实体/进程映像:(静态)
-
PCB程序控制块:描述进程的各种信息;进程存在的唯一标志; 操作系统所需的数据都在PCB中
-
程序段
-
数据段
进程的特征:
-
动态性:进程的最基本特征
-
并发性
-
独立性:系统资源分配的基本单位
-
异步性
-
结构性
进程的状态
基本状态:
-
就绪:万事俱备,只缺CPU
-
运行:占有CPU
-
阻塞:因等待某一事件暂时不能运行
-
创建态:操作系统为进程分配资源,初始化PCB
-
终止态:操作系统回收进程资源,撤销PCB
进程控制
实现进程状态的转换 ,通过原语(一种执行期间不允许中断的特殊程序,采用开中断和关中断)实现
原语作用:
-
更新PCB
-
将PCB插入合适队列
-
分配/回收资源
阻塞和唤醒原语要成对使用
进程通信
进程之间的信息交换
-
进程与进程之间拥有相互独立的地址空间
-
为了安全,进程之间不能直接访问
共享存储
两个进程对共享空间的访问必须是互斥的
-
基于数据结构的共享:速度慢、限制多,是一种低级通信
-
基于存储区的共享:比基于数据结构共享速度快,是一种高级通信
消息传递
进程间的数据交换以格式化的消息为单位,通过发送/接收原语实现
-
直接通信方式:消息直接挂到接收进程的消息缓冲队列上
-
间接通信方式/信箱通信方式:先发到中间实体(信箱)中
管道通信
在内存中开辟了一个大小固定的缓冲区
-
管道只能采用半双工通信
-
各进程要互斥访问管道
-
如果没写满,就不允许读;没读空,就不允许写
-
写满时,写进程的write()系统调用被阻塞;变空时,读进程的read()系统调用被阻塞
-
数据一旦被读出,就意味着被丢弃,因此,读进程最多只能有一个
进程同步、互斥
进程同步:直接制约关系 :为完成某种任务而建立的两个或多个进程,在某些位置上需要协调工作次序而产生的的制约关系
进程互斥:间接制约关系: 一个进程访问某临界资源时,另一个想访问该临界资源的进程必须等待,只有当访问结束资源释放后才能访问
-
进入区:检查是否可以进入临界区,设置正在访问临界资源 标志(上锁)
-
临界区:访问临界资源的代码
-
退出区:解除正在访问临界资源 标志(解锁)
-
剩余区:其他处理
遵循原则:
-
空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区
-
忙则等待:当已有进程进入临界区时,其他试图进入临界区的进程必须等待
-
有限等待:对请求访问的进程,应保证能在有限时间内进入临界区(保证不会饥饿)
-
让权等待:当进程不能进入临界区时,应立即释放处理机,防止进程忙等待
进程互斥的软件实现
单标志法
进程访问完临界区后,会把使用临界区的权限转交给另一个进程,即每个进程进入临界区的权限只能被另一个进程赋予
可以实现同一时刻最多只允许一个进程访问临界区
若P1在处理机运行,会因为turn!=1进入while循环直至时间片用完,切换至P0;
直到P0访问完毕将turn赋值为1,P1进程才可以访问临界区
缺:违背空闲让进原则:若P0一直不使用临界区,则P1将无法使用
双标志先检查
设置一个布尔型数组flag[],用来标记各个进程想要进入临界区的意愿, 每个进程在进入临界区之前,先检查当前有没有别的进程想进入临界区,若无,则把自身flag[]标志设置为true,并开始访问临界区
先检查,后上锁
由于异步性问题,①、②执行完毕切换到P1进程执行⑤、⑥
缺:违背忙则等待原则
双标志后检查
先上锁后检查
若按①⑤②⑥执行顺序
优: 解决了忙则等待问题
缺: 违背了空闲让进原则、有限等待原则,会造成饥饿
Peterson算法
若两个进程都想进入临界区,尝试孔融让梨,主动让对方先使用
- flag[0]=true; 表示自己想进入临界区
- turn =1; 优先让对方进入
- while(flag[1]&&turn==1); 若对方也想进,自己则等待
优:遵循空闲让进、忙则等待、优先等待原则
缺:违背了让权等待
进程互斥的硬件实现
中断屏蔽: 利用开关/中断原语实现
- 优:简单、高效
- 缺:不适合多处理机,只适用于操作系统内核进程,不适用于用户进程(开关/中断指令只能运行在内核态)
TS/TSL指令: 用硬件实现,执行过程不允许被中断
相比于软件实现方法,TSL指令把检查和上锁用硬件方式变成一气呵成的原子操作
- 优:实现简单,无需检查是否像软件实现方法一样具有逻辑漏洞;适用于多处理机环境
- 缺:违背让权等待原则
Swap/Exchange/XCHG指令:用硬件实现,执行过程不允许被中断
交换两个变量的值
-
优:实现简单,无需检查是否像软件实现方法一样具有逻辑漏洞;适用于多处理机环境
-
缺:违背让权等待原则
信号量机制
用户进程可以使用操作系统提供的一对原语(wait(S)/P(S),signal(S)/V(S))来对信号量进行操作,从而很方便进程互斥、进程同步
-
整型信号量:用整数表示系统中某种资源的数量
(只能进程初始化、P、V操作)
- 优:检查、上锁一气呵成,避免了并发、异步导致的问题
- 缺:违背让权等待
-
记录型信号量
在signal中,若S.value≤0表示等待队列中有进程在等待资源,因此使用wakeup唤醒队头进程
优:
-
遵循让权等待原则: 当P操作发现S.value<0,表示资源分配完毕,用block原语进行自我阻塞,主动放弃处理机
-
实现进程同步、进程互斥
-
实现对系统资源的申请和释放
若未特别说明,则P、V操作中的S指的是记录型信号量
-
信号量机制实现进程互斥
-
划定临界区
-
设置互斥信号量 mutex 初值为1 ;不同的临界资源需要设置不同的互斥信号量
-
临界区之前执行P(mutex )
-
临界区之后执行V(mutex );P、V操作必须成对出现
信号量机制实现进程同步
让本来异步并发的进程相互配合,有序推进(保证一前一后执行)
-
设置同步信号量S,初值为0
-
在前操作之后执行V(S)
-
在后操作之前执行P(S)
例:代码4在代码2后执行
信号量机制实现前驱关系
-
为每一对前驱关系各设置一个同步变量
-
在前操作之后对应的同步变量执行V操作
-
在后操作之前对应的同步变量执行P操作
生产者-消费者
系统中有一组生产者和一组消费者,生产者每次生产一个产品放入缓冲区,消费者每次从缓冲区(临界资源:互斥访问)取出一个产品并使用
- 临界区互斥访问
- 空时:先在缓冲区放入产品,再从缓冲区取出产品
- 满时:先消耗完产品,再生产产品
semaphore mutex=1; //互斥信号量,对缓冲区互斥访问
semaphore empty=n; //同步信号量,缓冲区的大小
semaphore full=0; //同步信号量,产品数量
producer(){
while(1){
生产产品;
P(empty);
P(mutex);
产品放入缓冲区; //缓冲区互斥访问
V(mutex);
V(full);
}
}
consumer(){
while(1){
P(full);
P(mutex);
缓冲区取产品; //缓冲区互斥访问
V(mutex);
V(empty);
消耗产品;
}
}
-
实现互斥的P操作一定要在实现同步的P操作之后
-
V操作的顺序可以交换
-
若缓冲区大小>1,则必须设置一个互斥信号量来保证互斥访问
多(类)生产者-多消费者
例:
桌子上有一只盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等着吃盘子中的橘子,女儿专等着吃盘子中的苹果。只有盘子空时,爸爸或妈妈才可向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出水果。
互斥关系:对盘子的访问
同步关系:
- 父亲在盘子放入苹果后,女儿才可以取苹果
- 母亲在盘子放入橘子后,儿子才可以取橘子
- 盘子为空(可由女儿/儿子触发),父亲母亲才可以放入水果
semaphore mutex = 1; //实现互斥访问盘子(缓冲区)
semaphore apple = 0 ; //盘子中有几个苹果
semaphore orange = 0; //盘子中有几个橘子
semaphore plate = 1; //盘子中还可以放多少个水果
dad(){
while(1){
准备一个苹果;
P(plate);
P(mutex);
把苹果放入盘子;
V(mutex);
V(apple);
}}
mom(){
while(1){
准备一个橘子;
P(plate);
P(mutex);
把橘子放入盘子;
V(mutex);
V(orange);
}}
daughter(){
while(1){
P(apple);
P(mutex);
从盘中取出苹果;
V(mutex);
V(plate);
吃苹果;
}}
son(){
while(1){
P(orange);
P(mutex);
从盘中取出橘子;
V(mutex);
V(plate);
吃橘子;
}}
抽烟者问题
假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料再桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)
互斥关系:访问桌子
同步关系:
- 桌子上有纸+胶水,第一个抽烟者取走
- 桌子上有烟草+胶水,第二个抽烟者取走
- 桌子上有纸+烟草,第三个抽烟者取走
- 抽烟者完成,供应者将材料放在桌上
semaphore offer1 = 0;
semaphore offer2 = 0;
semaphore offer3 = 0;
semaphore finish = 0; //抽烟是否完成
int i=0;
provider(){
while(1){
if(i==0) 将纸+胶水放在桌上;V(offer 1);
else if(i==1) 将烟草+胶水放在桌上;V(offer 2);
else if(i==2) 将纸+烟草放在桌上; V(offer 3);
P(finish);i=(i+1)%3;
}
}
smoker1(){
while(1){
P(offer 1);
取材料;抽烟;
V(finish);
}
}
smoke2(){
while(1){
P(offer 2);
取材料;抽烟;
V(finish);
}
}
smoker3(){
while(1){
P(offer 3);
取材料;抽烟;
V(finish);
}
}
读者写者问题
有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不
会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应让已有的读者和写者全部退出。
互斥关系:
-
读-写进程
-
写-写进程
semaphore rw =1; //实现对文件的互斥访问
int count =0; //当前有几个读进程在访问文件
semaphore mutex=1; //对count变量的互斥访问
semaphore w=1; //用于实现写优先
write(){
while(1){
P (w);
P(rw);
写文件;
V(rw);
V(w);
}
}
reader(){
while(1){
P(w);
P(mutex);
if(count==0) P(rw); //第一个读进程进行加锁
count++;
V(mutex);
V(w);
读文件;
P(mutex);
count--;
if(count==0) V(rw); //最后一个读进程进行解锁
V(mutex);
}
}
为了防止进程切换时另外的进程也进行P操作,因此P操作和count需要一起进行
哲学家进餐问题
一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。
互斥关系:相邻哲学家对筷子的访问是互斥的
对五名哲学家编号0~4对应数组下标
防止死锁现象:
-
最多允许4名哲学家同时进餐
-
奇数号哲学家拿左边筷子,偶数号哲学家拿右边筷子;没拿到的一方会进入阻塞
semaphore chopsticks[5]={1,1,1,1,1}
semaphore mutex=1 //互斥取筷子
Pi(){
while(1){
P(mutex);
P(chopsticks[i]); //左边筷子
P(chopsticks[(i+1)% 5 ] ); //右边筷子
V(mutex);
吃饭;
V(chopsticks[i]);
V(chopsticks[(i+1)% 5 ] );
思考;
}
}
死锁
- 死锁:在并发环境下,各进程因竞争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,都无法向前推进
- 饥饿:长期得不到想要的资源,某进程无法向前推进
- 死循环:某进程执行过程中一直跳不出某个循环
共同点 | 区别 | |
---|---|---|
死锁 | 进程无法顺利向前推进 | 至少有两个或两个以上进程同时发生死锁;发生死锁的进程一定处于阻塞态 |
饥饿 | 可能只有一个进程饥饿;发生饥饿的进程可能处于就绪态或阻塞态 | |
死循环 | 死循环的进程可以在处理机运行(运行态) |
死锁必要条件
-
互斥条件
-
不剥夺条件:不能由其他进程强行剥夺,只能主动释放
-
请求和保持:已经保持了至少一个资源,又提出了新的资源请求 ,而该资源又被其他进程占有并对自己的资源保持不放
-
循环等待:存在一种进程资源的循环等待链,链中每一个进程已获得资源的同时被下一个进程所请求
(发生死锁时一定有循环等待,但发生循环等待未必死锁(同类资源数>1))
发生死锁时机
-
各进程对不可剥夺的资源的竞争
-
进程推进顺序非法
-
请求和释放资源顺序不当
-
信号量使用不当:互斥的P操作在同步的P操作之前
处理死锁的策略
-
(静态)预防死锁:破坏死锁的四个必要条件中的一个或多个
-
互斥条件:使用SPOOLing技术可以将独占设备从逻辑上改成共享设备
-
不剥夺条件:改成资源可剥夺
-
请求和保持:
- 静态分配方法:进程运行前一次性申请完所需的全部资源,尚未满足前不允许投入运行,资源利用率低,可能导致饥饿
-
循环等待:
-
顺序资源分配法:给系统中资源编号,每个进程必须按编号递增的顺序请求资源 一个进程只有已占有小编号的资源时,才有资格申请更大编号的资源,因此有大编号资源的进程不可能逆向回来申请小编号的资源,从而就不会产生循环等待的现象;
缺点:
-
不方便增加新的设备(可能需要重写申请编号)
-
进程实际使用资源顺序和编号递增顺序不一致,会导致资源的浪费
-
用户编程麻烦
-
-
-
-
(动态)避免死锁:用某种方法防止系统进入不安全的状态(银行家算法)
安全序列:系统按照这种序列分配资源,每个进程都能顺利完成
安全性算法:检查当前的剩余可用资源,能否满足某个资源的最大需求,如果可以就把该进程加入安全序列,并把该进程所具有的资源回收;不断重复看最终是否让所有进程都加入安全序列
(处于安全状态一定不会发生死锁,处于不安全状态不一定会发生死锁)
-
死锁的检测和解除:允许死锁发生,操作系统检测到死锁发生会产生某种措施解除死锁
-
检测:
-
进程结点:对应一个进程
-
资源结点:对应一类资源
-
进程结点->资源结点:进程想申请几个资源
-
资源结点->进程结点:为进程分配了几个资源
若最终能消除所有边,就称这个图是可完全简化,此时一定没有死锁发生;若不能消除所有边则发生死锁
-
-
-
消除:一旦检测出死锁的发生,就应该立即解除死锁
-
资源剥夺法:挂起某些死锁进程,并强占它的资源;应防止饥饿产生
-
撤销进程法(终止进程法):强制撤销部分或者全部死锁的进程,并剥夺这些进程的资源
-
进程回退法:让一个或多个进程回退到足以避免死锁的地步,
-
线程
- 程序执行流的最小单位
- 基本的CPU执行单元
- 线程之间可以并发,提高了并发性,且线程间的并发,不需要切换进程环境,系统开销小
- 进程:资源分配的基本单位;线程:处理机调度的基本单位
- 几乎不拥有系统资源
-
用户级线程ULT:应用程序通过线程库实现,所有的线程管理工作都由应用程序负责,无需操作系统干预
(只有用户看得见,对操作系统是透明的)
-
内核级线程KLT:线程管理工作由操作系统内核在核心态下完成
(只有操作系统看得见)
因此,内核级线程才是处理机分配的单位
多线程模型
多对一模型
多个用户级线程映射到一个内核级线程,每个用户进程只对应一个内核级线程
- 优:开销小,效率高
- 缺:
- 并发度不高:当一个用户级线程被阻塞后,整个进程都会被阻塞
- 多个线程不可以在多核处理机上并行运行
一对一模型
一个用户级线程映射到一个内核级线程,每个用户进程都有与用户级线程同数量的内核级线程
- 优:并发能力强:一个线程被阻塞后,别的线程还可以继续执行
- 缺:线程管理成本高,开销大 ,一个用户进程占用太多内核级线程
多对多
n个用户级线程映射到m个内核级线程,每个用户进程对应m个内核级线程
克服了多对一模型并发度不高的问题;也克服了一对一模型中一个用户进程占用太多内核级线程的问题
-
用户级线程ULT:应用程序通过线程库实现,所有的线程管理工作都由应用程序负责,无需操作系统干预
(只有用户看得见,对操作系统是透明的)
-
内核级线程KLT:线程管理工作由操作系统内核在核心态下完成
(只有操作系统看得见)
因此,内核级线程才是处理机分配的单位
多线程模型
多对一模型
多个用户级线程映射到一个内核级线程,每个用户进程只对应一个内核级线程
- 优:开销小,效率高
- 缺:
- 并发度不高:当一个用户级线程被阻塞后,整个进程都会被阻塞
- 多个线程不可以在多核处理机上并行运行
一对一模型
一个用户级线程映射到一个内核级线程,每个用户进程都有与用户级线程同数量的内核级线程
- 优:并发能力强:一个线程被阻塞后,别的线程还可以继续执行
- 缺:线程管理成本高,开销大 ,一个用户进程占用太多内核级线程
多对多
n个用户级线程映射到m个内核级线程,每个用户进程对应m个内核级线程
克服了多对一模型并发度不高的问题;也克服了一对一模型中一个用户进程占用太多内核级线程的问题
管程
一种高级同步机制,让写程序时不需要再关注复杂的P、V操作
管程一种特殊的软件模块:(类)
-
需要在管程中定义共享数据
-
需要在管程中定义用于访问这些共享数据的入口(函数)
-
只有通过特定的入口(封装)才能访问共享数据
-
管程中有很多入口,但每次只能开放其中一个入口,并且只能让其中一个进程或线程进入
(由编译器负责实现各进程互斥进入管程的过程)
-
可以在管程中设置条件变量及等待/唤醒操作以解决同步问题,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本