【操作系统】 - 进程管理 - 4
1 进程间通信与同步
2 经典的ipc问题
3 进程调度
2.3 进程间通信与同步
进程间通信(InterProcess Communication IPC): 进程之间的信息交流与协调。
并发进程之间的两种关系
- 相互独立:进程之间没有任何关联关系(直接的或间接的), 仅有cpu
竞争关系。无需通信,由进程调度器来协调
- 相互关联:进程之间存在着某种关联关系(直接或间接),需要相互通信。
例如:共享内存变量/共享软硬件资源/数据传递/协调工作等
需要考虑的问题:
- 进程间如何通信呢,如何来相互传递信息呢?
- 当两个或多个进程在访问共享资源时,如何确保它们不会相互妨碍—
进程互斥问题;
- 当进程之间存在着某种依存关系时,如何来调整它们运行的先后顺序 – 进程同步问题
- 生活中的例子:教室座位/打饭窗口/一组同学做大作业
进程间通信方式
低级通信:只能传递状态和整数值(控制信息)
- 信号量
- 信号
高级通信:能够传递任意数量的数据
- 共享内存
- 消息传递
- 管道
共享内存:
绝大多数现代的操作系统都提高了相应的方法,来让各个进程共享它们地址空间当中的某些部分,即共享内存。在共享内存中,可以任意读写和使用任意的数据结构
一组进程向共享内存中写,另一组进程从共享内存中读,通过这种方式实现两组
进程间的信息交换。
消息传递
消息:由若干数据位组成。
消息传递:进程之间通过发送和接收消息来交换信息
消息机制由操作系统来维护,包括定义寻址方式,认证协议,消息的大小等。一般提供两个操作:
- Send() 发送一条消息
- Receive () 接收一条消息
如果两个进程P和Q想要进行通信,它们需要
- 在两者之间建立一个通信链路
- 使用Send()和Receive()交换信息
管道
管道通信由unix首创,由于其有效性,后来的一些系统相继引入了管道技术
管道通信以文件系统为基础,所谓管道即连接两个进程之间的一个打开的共享
文件,专用于进程之间的数据通信;
发送进程从管道的一端写入数据流,接收进程从管道的另一端按先进先出的顺序读出数据流
管道的读写操作即为文件操作fwrite / fread, 数据流的长度和格式没有限制
2.3.2 进程的互斥
进程互斥的产生原因:
进程宏观上并发执行,依靠时钟中断来实现微观上轮流执行;
访问共享资源
两个
两个或多个进程对同一共享数据同时进行读写操作,而最后的结果是不可预测的,它取决于各个进程具体运行情况。
在同一时刻,只允许一个进程访问该共享数据,即如果当前已有一个进程正在使用该数据
那么,其他进程暂时不能访问。这就是互斥的概念。
竞争状态问题的抽象描述
把一个进程在运行过程中所做的事情分为两类:
进程内部的计算或其他的一些事情,肯定不会导致竞争状态的出现;
对共享内存或共享文件的访问,可能会导致竞争状态的出现。我们把完成这类失去的那段程序称为“临界区”,把需要互斥访问的共享资源 称为“临界资源”
如果我们能设计出某种方法,使得任何两个进程都不会同时出现在临界区中,就可以避免竞争状态的出现。
实现互斥访问的四个条件
1 任何两个进程都不能同时进入临界区
2 不能事先假定cpu的个数和运行速度
3 当一个进程运行在它的临界区外面时,不能妨碍其他的进程进入临界区
4 任何一个进程进入临界区的要求应该在有限时间内得到满足。
2.3.3 基于关闭中断的互斥实现
当一个进程进入临界区后,关闭所有的中断;当它退出临界区时,再打开中断。
进程的切换是由中断引发的,关闭中断后,cpu不会被分配给其他进程,其他进程无法执行
操作系统内核经常使用这种方法来更新内部的数据结构(变量,链表等)
问题:
如果进程在临界区中执行大量的计算,结果会如何?忙等
这种方法能否用于用户进程?不适用
这种方法能否用在多cpu的系统中?不可以
2.3.4 基于繁忙等待的互斥实现
方法1. 加锁标志位
Lock的初始值为0,当一个进程想进入临界区时,先查看lock的值,若为1,说明已有进程在临界区内,只好循环等待。等它变成0,才可进入。每个进程的操作类似。例如,
图书馆借书。
缺点:可能出现针对lock的竞争状态问题。
方法2 强制轮流法
基本思想:每个进程严格的按照轮流的顺序来进入临界区。
优点:保证在任何时刻最多只有一个进程在临界区
缺点:违反了互斥访问四条件中的第三个条件。
方法3 Peterson方法:当一个进程想进入临界区时,先调用enter_region函数,判断
是否能安全进入,不能的话等待;当它从临界区退出后需要调用leave_region函数,
允许它进入临界区。两个函数的参数均为进程号。
Peterson算法解决了互斥访问的问题,而且克服了强制轮流法的缺点,可以完全
正常的工作。
TSL指令
加锁标志为的缺点在于可能出现针对共享变量lock的竞争状态。例如:
当进程0执行完循环判断语句后,被时钟中断打断,从而可能使多个进程
同时进入临界区。
能不能把查询lock变量与修改lock变量的这两个操作捆绑在一起,使它们不会被打断?这
就是硬件上的TSL(Test and Set Lock)指令
使用LOCK来作为加锁标志位,协调各个进程对共享资源的访问;
当LOCK为0时,任何进程均可使用TSL指令把它设置为非0,进而访问共享资源
当LOCK为非0时,循环等待;
在退出临界区时, 把LOCK设置为0
以上的各种方法,都是基于繁忙等待(busy waiting)
的策略,都可归纳为一种形式:当一个进程想要进入它的临界区时,首先检查一下是否允许它进入,若允许,就直接进入了;若不允许,就在那里循环的等待,一直等到运行
它进入
缺点:
- 浪费cpu时间
- 可能导致预料之外的结果(如:一个第优先级进程位于临界区中,这时有一个高优先级的进程也试图进入临界区)
一个低优先级的进程正在临界区中
另一个高优先级的进程就绪了
调度器把cpu分配给高优先级的进程
该进程也想进入临界区
高优先级进程将会循环等待,等待低优先级进程退出临界区
低优先级进程无法获得CPU,无法离开临界区
现有的进程互斥问题形式:两个或多个进程都想进入各自的临界区,但在任何时刻,只允许一个进程进入临界区
新的进程互斥问题形式:两个或多个进程都想进入各自的临界区,但在任何时刻,只允许N个进程同时进入临界区(N >= 1)
2.3.4 信号量
1965年由著名的荷兰计算机科学家Dijkstra提出,其基本思想是用一种新的变量类型来记录当前可用资源的数量。
有两种实现方式:
- semaphre的取值必须大于或等于0, 0表示当前已没有空闲资源,而正数表示当前空闲资源的数量
- semaphre的取值可正可负,负数的绝对值表示正在等待进入临界区的进程个数
- 信号量是由操作系统来维护的,用户进程只能通过初始化和两个标准原语】
(P, V原语)来访问。
初始化可指定一个非负整数,即空闲资源总数。
P, V原语包含有进程的阻塞和唤醒机制,因此在进程等待进入临界区时不会浪费cpu时间
P原语:P是荷兰语Proberen(测试)的首字母。申请一个空闲资源(把信号量减1),
若成功,则退出,若失败,则该进程被阻塞
V原语: V是荷兰语Verhogen(增加)的首字母。释放一个被占用的资源(把
信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之
2.3.5 进程的同步
进程间的同步是指多个进程中发生的事件存在某种时序关系,因此在各个进程之间
必须协同合作,互相配合,使各个进程按一定的速度执行,以完成某一项任务
同步: 合作
互斥: 竞争
只考虑基于信号量的同步问题
2.4 经典的ipc问题
1 生产者 – 消费者问题
2 哲学家就餐问题
3 读写者问题
用信号量来解决,主要问题:
如何选择信号量,如何安排P, V原语的顺序
1 生产者 – 消费者问题
问题描述:
两个进程(生产者和消费者)共享一个共有的,固定大小的缓冲区,生产者
不断地制造产品,并把它放入缓冲区,而消费者不断的把产品取出来,并使用它,要求这两个进程互相协调,正确的完成各自的工作
问题分析:
对于生产者进程:制造一个产品,当要送入缓冲区时,要检查缓冲区是否有空位,若是,才可将产品送入缓冲区,并在必要时通知消费者,否则等待
对于消费者进程:当它去取产品时,先要检查缓冲区中是否有产品可取,若有,则取走一个,并在必要时通知生产者,否则等待
这种相互等待,并通知信息就是典型的进程同步。
同时,缓冲区是一个临界资源,因此,各个进程在使用缓冲区的时候,还有一个互斥问题
哲学家就餐问题
1965年,由Dijkstra提出并解决,后来逐渐成为该领域的一个经典问题,或者说,是一块试金石,用来实验新的进程同步方法的优劣。
每个哲学家的动作只有两种:进餐和思考。当一个哲学家感到饥饿时,他试图去获取他左边和右边的两把叉子(一次取一把,顺序无关),然后才能开始进餐。吃完以后,他需要把两把叉子放回原处,然后继续思考
问题是:如何保证哲学家梦的动作有序进行?
- 必须有一个数据结构,来描述每个哲学家的当前状态
- 该数据结构是一个临界资源,各个哲学家对它的访问应该互斥的进行
- 进程互斥
- 3 一个哲学家吃饱后,可能要唤醒它的左邻右舍,两者之间存在着同步关系 – 进程同步
读写者问题
问题描述
在一个航空订票系统当中,有很多个竞争的进程想要访问(读,写)系统的数据库
访问规则是:在任何时候,可以允许多个进程同时来读,但如果有一个进程想要更新(写)
)该数据库,则其他的任何进程都不能访问,包括读者和写者,问题是:
怎样来编程实现读者和写者
问题分析:
任何时候“写者”最多只允许一个,而读者可以有多个
“读 – 写”是互斥的
“写 – 写”是互斥的
“读 – 读”是允许的
对于基于读者优先策略的方法,只要有一个读者处于活动状态,后来的读者都会被接纳。
如果读者源源不断的出现的话,那么写者就终止处于阻塞状态。
’