王道计算机考研 操作系统 —— 课程链接
2.3 进程同步 进程互斥
进程同步的概念
异步性导致进程以不可预知的速度向前推进。但是有时必须以一定的顺序执行进程。
进程互斥的概念
2.3.2 进程互斥的软件实现方式
单标志法
双标志先检查法
双标志后检查法
⭐并发执行的异步性往往会导致算法出现问题
Peterson算法
2.3.3 进程互斥的硬件实现方法
中断屏蔽方法
缺点:关中断指令只对执行关中断指令的处理机有效,在多处理机系统中,如果在一个处理机上执行了关中断指令,但对另一个处理机来说,关中断指令不起作用,还是会正常地切换进程,如果此时有一个进程要访问临界区,就有可能发生两个处理机上的两个进程同时访问临界区。
开关中断指令权限非常大,属于特权指令,需要在内核态运行。
TS指令(TSL指令)
“上锁”和“检查”变成原子操作,避免了软件实现方式中的异步逻辑漏洞
Swap指令
信号量机制
用户进程可以通过使用操作系统提供的一对原语
来对信号量
进行操作,从而很方便的实现了进程互斥、进程同步。
信号量
其实就是一个变量(可以是一个整数,也可以是更复杂的记录型变量) ,可以用一个信号量来表示系统中某种资源的数量,比如:系统中只有一台打印机,就可以设置一个初值为 1 的信号量。原语
是一种特殊的程序段,其执行只能一气呵成,不可被中断。原语是由关中断/开中断指令实现的。软件解决方案的主要问题是由“进入区的各种操作无法一气呵成”,因此如果能把进入区
、退出区
的操作都用“原语”实现,使这些操作能“一气呵成”就能避免问题。- 一对原语:
wait(S) 原语
和signal(S) 原语
,可以把原语理解为我们自己写的函数,函数名分别为wait
和signal
,括号里的信号量 S
其实就是函数调用时传入的一个参数。
wait、signal 原语常简称为P、V操作
(来自荷兰语 proberen 和 verhogen)。因此,做题的时候常把wait(S)、signal(S) 两个操作分别写为P(S)、V(S)
整型信号量
信号量与普通整数变量的区别:对信号量的操作只有三种,即 初始化
、P操作
、V操作
wait原语中,“检查”和“上锁”一气呵成,避免了并发、异步导致的问题
存在的问题:不满足“让权等待”原则,会发生“忙等”
记录型信号量
2.3.5 信号量机制实现进程互斥、同步、前驱关系
2.3.6 生产者消费者问题
同一时间只能有一个进程对临界资源进行“放”或“取”操作,如果不能互斥地访问,可能会导致两个生产者进程向同一个位置写入数据,导致数据覆盖,或两个消费者进程“取”了同一个产品
Q:生产产品和使用产品的代码是否可以放到PV操作之间?
A:从逻辑上来看,这么做是没有问题的(不会导致死锁、忙等或信号量出错),但这会导致临界区的代码量变大进程在访问临界区时就需要耗费更长的时间,使其他要访问临界区的进程等待时间变长。因此把这些非必要的代码放入临界区,会导致进程之间的并发度降低。
2.3.7 多生产者、多消费者模型
与生产者-消费者模型不同之处在于,多生产者-多消费者中,生产者生产/消费者消费的东西类型是不同的。
“多”是指“多类”,而非“多个”
Q:可以不设置互斥信号量吗?
A:即使不设置专门的互斥变量mutex,也不会出现多个进程同时访问盘子的现象
原因在于:本题中的缓冲区大小为1,在任何时刻,apple、orange、plate 三个同步信号量中最多只有一个是1。因此在任何时刻,最多只有一个进程的P操作不会被阻塞,并顺利地进入临界区
但如果盘子(缓冲区)容量大于1 :
分析多生产者-多消费者问题的关键:
从事件的角度考虑——某个事件必须发生在另一个事件之前
2.3.8 吸烟者问题
Q:是否需要设置一个专门的互斥信号量?
A:缓冲区大小为1,同一时刻,四个同步信号量中至多有一个的值为1,所以最多只有一个进程进入缓冲区,不会发生两个进程同时在缓冲区操作。
2.3.9 读者-写者问题
设置一个新的互斥变量mutex,完成count变量检查与赋值的“一气呵成”
2.3.10 哲学家进餐问题
当五位哲学家并发地拿起左边的筷子时,他们右边的筷子都被占用了,所以当执行“拿起右边筷子 ”时,所有的哲学家进程都会被阻塞,并循环地等待自己右边的人放下筷子,就出现了“死锁”,即每个进程都阻塞地互相等待对方释放资源,但又不会主动地释放自己手中的资源,就导致所有进程都无法往下推进
Q: 如何防止死锁的发生呢?
A:①可以对哲学家进程施加一些限制条件,比如最多允许四个哲学家同时进餐。这样可以保证至少有一个哲学家是可以拿到左右两只筷子的
②要求奇数号哲学家必须先拿左边的筷子,然后再拿右边的筷子,而偶数号哲学家先拿右边的筷子。用这种方法可以保证如果相邻的两个奇偶号哲学家都想吃饭,那么只会有其中一个可以拿起第一只筷子,另一个会直接阻塞。这就避免了占有一支后再等待另一只的情况。
③仅当一个哲学家左右两支筷子都可用时才允许他抓起筷子。
情况1:
情况2:
2.3.11 管程
为什么要引入管程
管程是一种 高级同步机制,是用来实现进程互斥和同步的
管程的定义和基本特征
管程是一种特殊的软件模块,有这些部分组成:
- 局部于管程的
共享数据结构
说明; - 对该数据结构进行操作的
一组过程
; - 对局部于管程的共享数据设置
初始值
的语句; - 管程有一个名字。
跨考Tips: “过程”其实就是“函数”
管程的定义有些类似于Java语言中的“类”
管程的基本特征:
- 局部于管程的数据只能被局部于管程的过程所访问;
- 一个进程只有通过调用管程内的过程才能进入管程访问共享数据;
管程中定义的共享数据结构只能被管程中定义的函数所修改,所以如果想要修改管程中的共享数据结构,只能通过调用管程提供的函数来间接地修改
3. 每次仅允许一个进程在管程内执行某个内部过程。(互斥地访问)
管程中设置条件变量
和等待/唤醒操作
,以解决同步问题
2.4 死锁
2.4.1死锁的概念
什么是死锁
死锁\饥饿\死循环的区别
死锁产生的必要条件
什么时候会发生死锁
思索的处理策略
2.4.2 死锁的处理策略—预防死锁
破坏互斥条件
破坏不剥夺条件
破坏请求和保持条件
破坏循环等待条件
发生等待时,只可能是持有小编号资源的进程等待大编号的资源,因而不会发生循环等待
2.4.3死锁的处理策略——避免死锁(银行家算法)
2.4.4 死锁的处理策略—死锁的检测与解除
如果系统中既不采取预防死锁的措施,也不采取避免死锁的措施,系统就很可能发生死锁。在这种情况下,系统应当提供两个算法:
①死锁检测算法:用于检测系统状态,以确定系统中是否发生了死锁。
②死锁解除算法:当认定系统中已经发生了死锁,利用该算法可将系统从死锁状态中解脱出来。