王道操作系统-进程管理速览

前言

本文是对王道操作系统课程的速览和总结,如有侵权,请联系我删除
本文结构是"知识点+展开",每一小节的展开顺序和知识点的顺序对应

进程的定义、组成、组织方式、特征

进程的定义
进程实体的组成
	PCB
	程序段
	数据段
进程的组织形式
	链接方式
	索引方式
进程的特征
	动态性
	并发性
	独立性
	异步性
	结构性

进程的定义:进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位

进程实体==进程映像

PCB是Process Control Block的缩写,是进程存在的唯一标志
PCB的组成
1.进程描述信息:进程标识符PID、用户标识符UID
2.进程控制和管理信息:进程当前状态、进程优先级
3.资源分配清单:程序段指针、数据段指针、键盘、鼠标
4.处理机相关信息:各种寄存器值

进程的组织方式
1.链接方式:将PCB分为多个队列,操作系统持有指向各个队列的指针
2.索引方式:建立PCB的索引表,操作系统持有指向各个索引表的指针

进程的特征
动态性:进程的最基本特征
独立性:进程是系统进行资源分配、调度的独立单位
异步性:各进程以不可预知的速度向前推进,可能导致运行结果的不确定性

补充:
1.进程封闭性
进程封闭性是指进程执行结果的结果只取决于进程本身,不受外界影响
也就是说,进程在执行过程中不管是不停顿地执行,还是走走停停,进程的执行速度都不会改变它的执行结果
失去封闭性后,不同速度下的执行结果不同
2.进程分段使用内存
C语言编写的程序在使用内存时一般分为三个段,它们一般是正文段(代码和全局赋值变量)、数据堆段数据栈段
二进制代码、全局赋值变量、常量存放在正文段
动态分配的存储区在数据堆段
临时使用的变量在数据栈段

进程的状态与转换

进程的五种状态
	运行态
	就绪态
	阻塞态
	创建态
	终止态
进程状态间的转换

运行态(Running):占有CPU资源、拥有其他所需资源
就绪态(Ready):没有CPU资源、拥有其他所需资源
阻塞态(Waiting/Blocked):没有CPU资源、没有其他所需资源
创建态(New):操作系统为新进程分配资源、创建PCB
终止态(Terminated):操作系统回收进程的资源、撤销PCB

就绪态->运行态:进程被调度
运行态->就绪态:时间片到,或CPU被其他高优先级的进程抢占
运行态->阻塞态:等待系统资源分配,或等待某事件发生(主动行为)
阻塞态->就绪态:资源分配到位,等待的事件发生(被动行为)
创建态->就绪态:系统完成创建进程相关的工作
运行态->终止态:进程运行结束,或运行中遇到不可修复的错误

进程控制

进程控制的基本概念
进程控制相关原语
	进程创建
	进程终止
	进程阻塞
	进程唤醒
	进程切换

进程控制的概念:进程控制就是要实现进程状态的转换

进程控制用原语实现

阻塞和唤醒要成对出现

进程通信

共享存储
	基于数据结构的共享
	基于存储区的共享
管道通信
消息传递
	直接通信方式
	间接通信方式

进程通信的概念:进程通信就是指进程之间的信息交换

共享存储:操作系统在内存中设置一个共享空间,供进程互斥地访问
共享存储的两种方式
1.基于数据结构的共享:共享空间中只能放特定的数据结构;这种共享方式速度慢,限制多,是一种低级通信方式
2.基于存储区的共享:共享空间中的数据形式,存放位置都由进程控制,而不是操作系统;这种共享方式速度快,是一种高级通信方式

管道通信:操作系统在内存中开辟一个固定大小的缓冲区(pipe),供进程互斥地访问
1.管道通信只能实现半双工通信,实现双向同时通信要建立两个管道
2.写满时,写进程阻塞;读空时,读进程阻塞
3.没写满,不能读;没读空,不能写
4.数据一旦被读出,就从管道中抛弃->读进程最多只能有一个

消息传递:进程间的数据交换以格式化的消息(Message)为单位,通过操作系统系统的"发送消息/接收消息"原语实现
格式化的消息==消息头+消息体
消息头包括:发送进程ID、接受进程ID、消息类型、消息长度等格式化信息
消息头就像信封,消息体就像信封里的信
消息传递的两种方式
1.直接通信方式:消息直接挂到接收进程的消息缓冲队列上
2.间接通信方式:消息要先发送到中间实体(信箱)中,因此间接通信方式也称"信箱通信方式"

线程概念和多线程模型

线程的概念
引入线程的原因
线程机制和传统进程机制的对比
	资源分配、处理机调度
	并发性
	实现并发的系统开销
线程的属性
线程的实现方式
	用户级线程
	内核级线程
	组合方式
多线程模型
	多对一模型
	一对一模型
	多对多模型

线程的概念:线程是一个基本的CPU执行单位,也是程序执行的最小单位,是为了解决传统进程只能串行地执行一系列程序而引入的机制

引入线程的原因
1.增加系统的并发度:引入线程后进程内的各线程之间也可以并发,提升了系统的并发度
2.减少并发带来的系统开销

线程机制和传统进程机制的对比
1.资源分配、处理机调度
1)传统进程机制中,进程是资源分配、处理机调度的基本单位
2)引入线程后,进程是资源分配的基本单位,线程是处理机调度的基本单位
2.并发性
1)传统进程机制中,只能进程间并发
2)引入线程后,各线程间也能并发,提高了系统的并发度
3.实现并发的系统开销
1)传统的进程间并发,需要切换进程的运行环境,系统开销大
2)引入线程后,如果是统一进程内的线程切换,则不需要切换进程环境,系统开销小
2)引入线程后,减小了并发所带来的系统开销

线程的属性
1.线程是处理机调度的单位
2.多CPU计算机中,各线程可以占用不同的CPU
3.每个线程都有一个线程ID,线程控制块(TCB,Thread Control Block)
4.线程也有就绪、阻塞、运行三种基本状态
5.对于同一进程的不同线程
1)共享进程资源
2)线程间通信无需系统干预
3)线程切换不会引起进程切换,因此线程切换的系统开销很小
6.对于不同进程中的不同线程,线程切换回引起进程切换,因此线程切换的系统开销很大

线程的实现方式
1.用户级线程(User-Level Thread):应用程序通过线程库实现的线程
线程切换工作由应用程序负责,因此用户级线程的切换在用户态下完成
2.内核级线程(Kernel-Level Thread):操作系统内核视角的线程
内核级线程的管理工作由操作系统内核完成,因此内核级线程的切换在核心态下完成
3.用户级线程和内核级线程的组合方式
1)将n个用户级线程映射到m个内核级线程上,显然n>=m,否则会造成系统资源的浪费
2)具体的映射方式由多线程模型决定
3)内核级线程才是处理机分配的单位,因此在多核计算机上运行的进程,最多只能有m个用户级线程并行执行

多线程模型
1.多对一模型:多个用户级线程对应一个内核级线程
优点:线程切换在用户态下完成,不需要切换到核心态,线程管理的系统开销小,效率高
缺点:只能有一个用户级线程并行执行,并发度不高,且一旦某个用户级线程被阻塞,整个进程都会被阻塞
2.一对一模型:用户级线程和内核级线程一一对应
优点:多线程可在多核处理机上并行执行,且当某个线程被阻塞时,其它线程可以继续执行,并发性高
缺点:用户进程占用过多的内核级线程,且内核级线程会频繁切换,因此需要CPU频繁切换到核心态,线程管理的系统开销大
3.多对多模型:将n个用户级线程映射到m个内核级线程上,显然n>=m
克服了多对一模型并发度不高的缺点,克服了一对一模型用户进程占用太多内核级线程的缺点,集二者之所长

处理机调度

基本概念
三个层次
	高级调度(作业调度)
	中级调度(内存调度)
	低级调度(进程调度)
三层调度的联系、对比
挂起态和七状态模型

处理机调度的概念:按照某种算法选择一个进程将处理机资源分配给它

处理机调度的三个层次
1.高级调度(作业调度):按照某种规则,从后备队列中选择合适的作业将其调入内存,并为其创建进程
高级调度会为作业创建PCB,使其获得竞争处理机资源的权利
2.中级调度(内存调度):按照某种规则,从挂起队列中选择合适的进程将其数据调回内存
1)中级调度的目的:提高内存利用率和系统吞吐量
2)中级调度不会将PCB一起调到外存,PCB会常驻内存
3.低级调度(进程调度):按照某种规则,从就绪队列中选择一个进程为其分配处理机资源
低级调度是操作系统中最基本的调度

三层调度的联系、对比
1.高级调度:作业从外存到内存的调入,每个作业只能调入一次,因此高级调度发生频率低
高级调度使进程从无状态->创建态->就绪态
2.中级调度:进程从外存到内存的调度,发生频率中
中级调度使进程从挂起态->就绪态/阻塞态
3.低级调度:进程从内存到CPU的调度,发生频率高
低级调度使进程从就绪态->运行态

挂起态:为了减轻系统负载,提高资源利用率,暂时不执行的进程会被调到外存从而变成"挂起态"
七状态模型:在五状态模型的基础上加入了"就绪挂起"和"阻塞挂起"两种状态
挂起和阻塞的区别
1.挂起态和阻塞态都暂时不能获得处理机资源
2.挂起态是将部分进程映像调到外存,而阻塞态的进程映像还在内存中

进程调度

进程调度的时机
进程切换
进程调度的方式

什么时候需要进程调度
1.运行态进程主动放弃处理机资源
1)进程正常终止
2)进程异常终止
3)进程阻塞
2.运行态进程被动放弃处理机资源
1)时间片用完
2)有更紧急的事情需要处理(如I/O中断)
3)有更高优先级的进程进入就绪队列

什么时候不能进行进程调度
1.在中断处理的过程中
2.进程在操作系统的内核程序临界区中
临界资源:各进程需要互斥地访问的资源
临界区:访问临界资源的代码
内核程序临界区一般是用来访问内核数据结构的,比如进程的就绪队列,因此在访问内核程序临界区期间不能进行进程调度与切换
3.原子操作过程中(原语)

进程切换的过程
1.保存原来进程的数据
2.恢复新进程的数据
进程调度和进程切换有代价,并不是调度越频繁,系统并发度越高

进程调度的方式
1.非剥夺调度方式(非抢占式):只能由当前运行的进程主动放弃CPU
非抢占式进程调度系统开销小,但是无法及时处理紧急任务,适合于早期的批处理系统
2.剥夺调度方式(抢占式):可由操作系统剥夺当前进程的CPU使用权
抢占式进程调度适合于分时操作系统、实时操作系统

调度算法的评价指标

CPU利用率
系统吞吐量
周转时间
等待时间
响应时间

周转时间=作业完成的时间-作业提交的时间
带权周转时间=作业周转时间/作业实际运行的时间

进程等待时间=等待被服务的时间之和
作业等待时间=作业在外存后备队列中等待的时间+建立进程后的等待时间
进程在等待I/O完成的期间不计入等待时间

早期批处理系统的调度算法

饥饿
先来先服务(FCFS)算法
短作业优先(SJF/SPF)算法
高响应比优先(HRRN)算法

饥饿:某进程/作业长期得不到服务
如果某进程/作业一直得不到服务,则称为"饿死"

以下三种调度算法,既可以用于作业调度,也可以用于进程调度

先来先服务算法(First Come First Serve)
非抢占式
优点:公平、算法实现简单,不会导致饥饿
缺点:排在长作业后面的短作业等待时间长,带权周转时间长,对长作业有利,对短作业不利

短作业优先算法(Shorest Job First)
非抢占式,但是也有抢占式的版本–最短剩余时间优先算法(SRTN,Shortest,Remaining Time Next)
优点:"最短的"平均等待时间、平均周转时间
缺点:不公平,对短作业有利,对长作业不利,可能产生饥饿;作业/进程的运行时间是由用户提供的,并不一定真实,不一定能做到真正的短作业优先

高响应比优先算法(Highest Response Ratio Next)
非抢占式
响应比=(等待时间+要求服务的时间)/要求服务的时间
响应比和带权周转时间的区别:
1.带权周转时间的静态的,作业/进程的一次执行过程中,带权周转时间是唯一确定的
2.响应比是动态的,外存后备队列中的作业或内存就绪队列中的进程的响应比,会随着时间的推移改变
优点:
1.等待时间相同时,要求服务时间短的优先(SJF算法的优点)
2.要求服务时间相同时,等待时间短的优先(FCFS算法的优点)
3.对于长作业来说,随着等待时间越来越久,其响应比也会越来越大,从而避免了长作业饥饿的问题

交互式系统的调度算法

时间片轮转调度(RR)算法
优先级调度算法
多级反馈队列调度算法

时间片轮转调度算法(Round-Robin)
抢占式
用于分时操作系统的进程调度,分时操作系统更注重"响应时间",因此不计算周转时间
时间片设置不合理的影响:
1·时间片太大,则RR算法会退化成FCFS算法,增大进程的响应时间
2.时间片太小,进程切换过于频繁,切换进程的开销过大
优点:公平、响应快,适用于分时操作系统,不会造成饥饿
缺点:高频率的进程切换有一定开销;不区分任务的紧急程度

优先级调度算法
抢占式、非抢占式都有
既可用于作业调度,也可用于进程调度
补充:
1.可以按照优先级组织多个就绪队列,也可以把优先级高的进程排在队头位置
2.优先级可以分为静态优先级和动态优先级
通常:
1.系统进程优先级高于用户进程
2.前台进程优先级高于后台进程
3.操作系统更偏好I/O型进程(I/O繁忙型进程),与I/O型进程对应的是计算型进程(CPU繁忙型进程)
I/O设备和CPU可以并行工作,优先调度I/O型进程可以提高系统资源利用率和吞吐量
优点:用优先级区分紧急程度、重要程度,适用于实时操作系统,可以灵活调整对各种作业/进程的偏好程度
缺点:若高优先级进程不断进入就绪队列,则可能导致饥饿

多级反馈队列调度算法
抢占式
优点:
1.对各类型进程相对公平(FCFS算法的优点)
2.每个新进程都可以很快得到响应(RR算法的优点)
3.短进程只用较短的时间就可以完成(SPF算法的优点)
4.不必估计进程的运行时间,避免用户作假
5.可灵活调整对各类进程的偏好程度
缺点:可能导致饥饿

进程同步和进程互斥

为什么需要进程同步
进程互斥
	什么是进程互斥
	四个部分
		进入区
		临界区
		退出区
		剩余区
	需要遵循的原则
		空闲让进
		忙则等待
		有限等待
		让权等待

进程同步,是并发执行的进程之间的直接制约关系
进程互斥,是并发执行的进程之间的间接制约关系

为什么需要进程同步
并发性带来了异步性,有时需要通过进程同步解决这种异步问题
有的进程之间需要相互配合地完成工作,各进程的工作推进需要遵循一定的先后顺序

进程互斥:对临界资源的访问,需要互斥的进行,即同一时间段内只能允许一个进程访问该资源
四个部分
1.进入区:检查是否可进入临界区,若可进入,需要"上锁"(设置正在访问临界资源的标志)
2.临界区(临界段):访问临界资源的那段代码
3.退出区:负责"解锁"(解除正在访问临界资源的标志)
4.剩余区:其余代码部分

进程互斥需要遵循的原则
1.空闲让进:临界区空闲时,应允许一个进程访问
2.忙则等待:临界区正在被访问时,其他试图访问的进程需要等待
3.有限等待:要在有限时间内进入临界区,保证不会饥饿
4.让权等待:进不了临界区的进程,要释放处理机,防止忙等待

进程互斥的实现方法

软件实现
	单标志法
	双标志先检查法
	双标志后检查法
	Peterson算法
硬件实现
	中断屏蔽
	TestAndSet指令
	Swap指令

进程互斥的软件实现方法:
1.单标志法:在进入区只做"检查",不"上锁",在退出区把临界区的使用权交给另一个进程
(相当于在退出区既给另一个进程解锁,又给自己上锁)
主要问题:不遵循"空闲让进"原则
2.双标志先检查法:在进入区先"检查"后"上锁",退出区"解锁"
主要问题:不遵循"忙则等待"原则
3.双标志后检查法:在进入区先"上锁"后"检查",退出区"解锁"
主要问题:不遵循"空闲让进、有限等待"原则,可能导致"饥饿"
4.Peterson算法:在进入区"主动争取-主动谦让-检查对方是否想进、己方是否谦让"
主要问题:不遵循"让权等待"原则,会发生"忙等"

进程互斥的硬件实现方法:
1.中断屏蔽:使用"开/关中断"指令实现
优点:简单高效
缺点:只适用于单处理机,只适用于操作系统内核进程
2.TestAndSet指令(TS指令/TSL指令):
1)先检查临界资源是否已被上锁
2)无论之前临界资源是否已被上锁,现在都将临界资源上锁
3)返回之前临界资源是否已被上锁
优点:实现简单,适用于多处理机环境
缺点:不满足"让权等待"
3.Swap指令(Exchange指令/XCHG指令):逻辑上同TSL指令

Peterson算法实现2个进程对临界资源的互斥访问

bool flag[2];	//flag[i]表示进程Pi是否有意愿进入临界区
int turn = 0;	//turn表示优先让哪个进程进入临界区

//P0进程
flag[0] = true;	//表明P0进程有意愿进入临界区
turn = 1;	//表明P0进程愿意优先让P1进程进入临界区
while (flag[1] && turn == 1);	//当P1进程有意愿进入临界区,且最后一次"谦让"的是P0进程,则P0进程忙等待
critical section;	//临界区
flag[0] = false;	//访问完临界区后,表明P0进程没有意愿进入临界区
remainder section; //剩余区

//P1进程
flag[1] = true;	//表明P1进程有意愿进入临界区
turn = 0;	//表明P1进程愿意优先让P0进程进入临界区
while (flag[0] && turn == 0);	//当P0进程有意愿进入临界区,且最后一次"谦让"的是P1进程,则P1进程忙等待
critical section;	//临界区
flag[1] = false;	//访问完临界区后,表明P1进程没有意愿进入临界区
remainder section; //剩余区

信号量机制

什么是信号量
wait/signal原语
整型信号量
记录型信号量
	什么条件下需要执行block/wakeup原语

信号量:用来表示系统中某种资源的数量的变量,可以是整数,也可以是记录型数据
wait原语:简称P操作,用来请求某种资源S
signal原语:简称V操作,用来释放某种资源S
wait(S)、signal(S)两个操作分别写作P(S)、V(S)

整型信号量:用一个整数型变量作为信号量,数值表示某种资源数
整型信号量与普通整型变量的区别:对信号量只能执行 初始化、P、V 三种操作
整型信号量存在的问题:不满足让权等待原则

记录型信号量:S.value表示某种资源数,S.L指向等待该资源的队列
S.value的初值表示系统中某种资源的总数目
P操作中,一定是先S.value–,当S.value<0时表示S资源已分配完毕,因此进程应调用block原语进行自我阻塞(因S资源不足而阻塞的进程会被插入S资源的等待队列S.L中)
V操作中,一定是先S.value++,若加1后S.value<=0,表示依然有进程在等待该类资源,因此应调用wakeup原语唤醒等待队列中的第一个进程(被wakeup原语唤醒的进程会被移出等待队列S.L)
可以用记录型信号量实现系统资源的"申请"和"释放"
可以用记录型信号量实现进程互斥、进程同步

/*记录型型号量的定义*/
typedef struct {
	int value;	//剩余资源数
	struct process*L;	//等待队列
}semaphore;

/*wait原语*/
void wait(semaphore S) {
	S.value--;
	if(S.value<0)
		block(S.L):
}

/*signal原语*/
void signal(semaphore S) {
	S.value++;
	if (S.value <= 0)
		wakeup(S.L);
}

信号量机制的应用

实现进程互斥
实现进程同步
实现进程的前驱关系

实现进程互斥
1.分析问题,确定临界区
2.设置互斥信号量mutex,初值为临界资源数量
3.临界区之前对信号量执行P操作
4.临界区之后对信号量执行V操作

实现进程同步
1.分析问题,找出哪里需要实现"一前一后"的同步关系
2.设置同步信号量,初始值为0
3.在"前操作"之后执行V操作
4.在"后操作"之前执行P操作

实现进程的前驱关系:前驱关系问题,本质上是多个同步问题的组合
1.分析问题,画出前驱图,把每一对前驱关系都看成一个同步问题
2.为每一对前驱关系设置同步信号量,初值为0
3.在每个"前操作"之后执行V操作
4.在每个"后操作"之前执行P操作

生产者-消费者问题

问题描述
生产者进程、消费者进程之间的同步、互斥关系
信号量设置
伪码描述
能否改变相邻P、V操作的顺序
多生产者-多消费者问题
吸烟者问题

问题描述
1.系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用
2.生产者、消费者共享一个初始为空、大小为n的缓冲区
3.只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待
4.只有缓冲区不空时,消费者才能从中取出产品,否则必须等待
5.缓冲区是临界资源,各进程必须互斥地访问

同步关系
1.缓冲区满时,生产者要等消费者取走产品
2.缓冲区空时,消费者要等生产者放入产品
互斥关系
生产者进程、消费者进程要互斥地访问缓冲区

/*信号量设置*/
semaphore mutex = 1;	//互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n;	//同步信号量,表示空闲缓冲区的数量
semaphore full = 0;		//同步信号量,表示产品的数量,也就是非空缓冲区的数量

/*伪码描述*/
//生产者进程
producer() {
	while (true) {
		produce;			//生产一个产品
		P(empty);			//消耗一个空闲缓冲区
		P(mutex);			//申请缓冲区访问权限
		critical section;	//把产品放入缓冲区
		V(mutex);			//交还缓冲区访问权限
		V(full);			//增加一个产品
	}
}

//消费者进程
consumer() {
	while (true) {
		P(full);			//消耗一个产品
		P(mutex);			//申请缓冲区访问权限
		critical section;	//从缓冲区中取走一个产品
		V(mutex);			//交还缓冲区访问权限
		V(empty);			//增加一个空闲缓冲区
		use product;		//使用产品
	}
}

能否改变相邻P、V操作的顺序
1.在生产者-消费者问题中,实现互斥的P操作一定要在实现同步的P操作之后,否则会发生死锁,因此不能改变相邻P操作的顺序
2.V操作不会导致进程阻塞,因此两个V操作顺序可以交换

多生产者-多消费者问题
生产者和多消费者共用一个缓冲区的问题
不能从单个进程行为的角度来考虑进程同步和互斥关系,而应该从"事件"的角度考虑

例:实现同步关系:{消费者进程1, 消费者进程2}的消费行为 => 空闲缓冲区增加 => {生产者进程1, 生产者进程}被唤醒

解:消费者进程1,消费者进程2都可以触发"事件":空闲缓冲区增加
"事件"缓冲区增加既可以唤醒生产者进程1,也可以唤醒生产者进程2
故实现上述同步关系只需要1个同步信号量

吸烟者问题:特殊的生产者-消费者问题
1.供应者可以提供多种不同的产品,以应对多种吸烟者的不同需求
2.吸烟者吸完烟后会反馈给供应者一个信号,供应者在收到反馈信号后才能继续提供产品
3.供应者按照一定的逻辑提供不同的产品,如按顺序轮流提供、随机提供

读者-写者问题

问题描述
读进程、写进程之间的互斥关系
信号量设置
伪码描述

读者-写者问题
1.允许多个读者可以同时对文件执行读操作
2.只允许一个写者往文件中写信息
3.任一写者在完成写操作之前不允许其他读者或写者工作
4.写者执行写操作前,应等待已有的读者和写者全部退出

互斥关系:写进程与写进程、写进程与读进程互斥访问文件
读进程与读进程之间不存在互斥关系

/*信号量设置*/
semaphore rw = 1;		//表示当前是否有进程在访问共享文件,用于实现对文件的互斥访问
int count = 0;			//记录当前有几个读进程在访问文件
semaphore mutex = 1;	//用于保证count变量的互斥访问
semaphore w = 1;		//用于实现"读写公平"

/*伪码描述*/

//写进程
writer() {
	while (true) {
		P(w);
		P(rw);	//写之前加锁
		写文件...
		V(rw);	//写之后解锁
		V(w);
	}
}

//读进程
reader() {
	while (true) {
		P(w);
		P(mutex);	//各读进程互斥访问count
		if (count == 0)
			P(rw);	//第一个读进程负责加锁
		count++;	//访问文件的读进程数+1
		V(mutex);
		V(w);
		读文件...
		P(mutex);
		count--;	//访问文件的读进程数-1
		if (count == 0)
			V(rw);	//最后一个读进程负责解锁
		V(mutex);
	}
}

代码分析
1.互斥信号量mutex的设置是为了使

if (count == 0)
	P(rw);
count++;

成为原子操作,此时count是临界资源
2.设置互斥信号量是为了实现"读写公平",防止写进程饥饿

哲学家进餐问题

问题描述
问题分析
哲学家进程互斥关系
防止死锁的实现方式

哲学家进餐问题
1.一张圆桌上坐着n个哲学家,每两个哲学家之间摆一根筷子
2.哲学家只会思考和进餐,哲学家在思考时,并不影响他人
3.哲学家进餐前,会试图拿起左、右两根筷子(一根一根地拿起)
4.如果筷子已在他人手上,则需等待,哲学家只有同时持有两根筷子才能开始进餐
5.哲学家进餐完毕后,放下筷子继续思考

问题分析
1.哲学家进程需要同时持有多个临界资源
2.哲学家进餐问题的关键在于解决进程死锁

互斥关系:每根筷子被相邻的两个哲学家互斥使用

防止死锁的实现方式
1.施加限制条件,如最多允许同时拿起k根筷子(k < n)
2.改变申请临界资源的逻辑,如要求奇数号哲学家优先拿左边的筷子,偶数号哲学家优先拿右边的筷子,使一半的筷子被"争抢"

/*施加限制条件的实现方式*/

/*信号量设置*/
semaphore chopstick[n] = { 1,1,... };	//互斥信号量数组,实现对n根筷子的互斥访问
semaphore mutex = k;					//最多允许同时拿起k根筷子

/*伪码描述*/
Pi() {									//i号哲学家进程
	while (true) {
		P(mutex);
		P(chopstick[left]);				//拿起左边的筷子
		P(chopstick[right]);			//拿起右边的筷子
		V(mutex);
		吃饭...
		V(chopstick[left]);				//放下左边的筷子
		V(chopstick[right]);			//放下右边的筷子
		思考...
	}
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值