操作系统(四)进程同步与互斥
进程间相互作用
在一个计算机系统中存在多个进程,这些进程直接可能存在着逻辑关系,可可能不存在逻辑关系,由于他们都要共享或竞争一个计算机系统中的资源,所以不可避免的会发生相互作用。
相关进程和无关进程
- 相关进程:在逻辑上具有某种联系的进程。即一个进程的执行不影响其他进程的执行,且与其他进程的进展无关,即他们是各自独立的,则说这些并发进程的相互之间没有关系。显示无关进程一定没有共享变量。他们都在各自的数据集合上操作。
- 无关进程:在逻辑上没有任何联系的进程。即一个进程的执行以来其他进程的进展情况,或者一个进程的执行可能会影响其他进程的执行,则说明这些并发进程是相关的。
时间有关的错误
一个进程由于自身或外界的原因而可能被中断,且断点是不固定的。
进程执行的进度是不能由进程自身控制的,对于相关进程来说,可能有若干并发进程同时共享资源,即一个进程一次使用未结束,另一进程也开始使用,形成交替使用共享资源。
与时间有关的错误:在并发程序中共享了公共变量,是的程序的计算与并发程序执行的速度有关。
如程序A 、B共享一个变量n,A每次对n进行+1操作,B每次打印n后将n设置为0,由于A、B都以各自独立的速度向前推进,所以会产生交叉运行。所以会参生三种情况
- A 在B的打印操作和n=0之前
- A 在B的打印操作之后,n=0之前
- A 在B的打印操作和n=0之后
进程的同步与互斥
进程是操作系统中可以独立运行的单位,在同一个系统中,进程之间不可避免地会产生某种联系,如竞争资源,而有些进程本就是为完成同一个作业而运行的。因此,进程之间必须相互协调,彼此之间交互信息,这就是进程之间一种简单的通信。
进程的同步
在计算机系统中,为了完成某一工作,不同进程间可能需要相互协作。
例如:进程A读取文件内容存入缓冲器,进程B从缓存器中读取并计算。他们共享缓冲器。
若A的执行速度远超B的执行速度,可能会在B还未读取结束时,将文件覆盖掉,照成记录丢失。如果B的执行速度远超A的执行速度时,又不可避免对同一记录进行加工,所以两个进程必须协调动作节奏,彼此同步。
要实现进程同步就必须实现一种机制,该机制不仅能把其他进程需要的消息发送出去,也能测试进程自己需要的信息是否到达,这种能实现进程同步的机制称为同步机制。
不同同步机制中实现的方法是不同的。
进程的互斥
在系统中,许多进程常常需要共享资源,而这些资源往往要求排他性的使用,即只为一个进程服务,因此各个进程只能互斥使用这些资源,进程间的关系就是进程的互斥。如多个进程竞争使用打印机。
进程的互斥是进程间的一种间接制约关系。
临界区
若系统中的某些资源一次只允许一个进程使用,则这类资源称为临界资源或共享变量,而在进程中访问临界资源的程序称为临界区。
如有若干程序共享某一临界区,则该临界区称为相关临界区。当一个进程在相关临界区执行时,如果不让另一进程进入相关的临界区执行,就不会形成多个进程对共享变量进行交叉访问,就可以避免出现关于时间的错误。
为保证系统中各并发程序顺利运行,对两个以上进入相关临界区的进程,必须实行互斥。系统对相关临界区的调度使用原则如下:
- 临界区为空时,有进程请求进入临界区,则允许进入(有效利用临界资源)
- 临界区中有一个进程时,其他要求进入临界区的进程必须等待(反应互斥的基本含义,即表示临界资源具有排他性)
- 临界区为空时,如有多个线程同时要求进入,则只允许一个线程进入。(3是1、2的一个特殊情况)
- 任一进程进入临界区的要求应在有限时间满足(避免进程发生忙等待)
- 处于等待状态的进程应放弃占用处理器(避免进程发生死锁)
信号量及P、V操作
信号量
用常规的程序来实现进程之间同步、互斥关系需要复杂的算法,而且会兆成忙等待,浪费处理器资源,为此引入信号量的概念。
信号量:一种特殊的变量,表面形式是一个整型变量附加一个队列。而且只能被特殊操作使用(P、V操作,二者都是原语)。是由进程互斥的关键含义抽象而来。信号量是被保护的量,只有P、V操作和信号量初始化操作才可访问和改变它的值。
P、V操作
P(S){
S = S -1;
}
S<0时,该进程进入等待状态,然后将该进程的PCB插入响应的S信号量等待队列末尾,
知道有其他进程在S上执行V操作为止。
V(S){
S = S +1;
}
S<=0时,释放S信号量队列中等待的一个进程,将其改为就绪状态,并将其插入就绪队列。
信号量与P、V操作的物理含义
P操作和V操作都是对信号量S进行的。
信号量S表示某类可用的临界资源。
- S>0时,S的大小表示可用临界资源的数目。
- S<0时,表示没有可分配的资源,S的绝对值为排在S信号量的等待队列进程中的数目
每进行一个P操作,表示请求的进程分配到了一个资源,每进行一次V操作,意味着进程释放了一个资源。
用P、V操作实现进程之间的互斥
假设进程A、B竞争进入临界区:
A:
P(S)
临界区操作
V(S)
B:
P(S)
临界区操作
V(S)
S的初始值为1
用P、V操作实现进程之间的同步
A:
把信息送入缓冲区;
V(S1)
P(S2)
B:
P(S1)
把信息从缓冲区取走;
V(S2)
S1、S2的初始值为0
信号量及P、V操作小结
P、V操作虽然在逻辑上完整,但也具有明显缺点:由于P、V操作每次只能进行加1或减1操作,当进程同时使用多个资源时,需进行多次P操作和V操作,这不仅增加了程序的复杂性,也降低了通信效率。致使进程之间需要相互等待很长的时间,甚至可能导致死锁的发生。
P、V操作必须成对出现,当为互斥操作时,他们处于同一进程,处于同步操作时,处于不同进程。
经典的进程同步问题
简单生产者—消费者问题
- 一个生产者进程 P
- 一个缓冲区
- 一个消费者进程Q
设置信号量empty,表示空缓冲区数量,初始值为1
设置信号量full,指示满缓冲区数量,初始值为0
同步解决方案:
P{
P(empty)
生产一个商品,送入缓冲区
V(full)
}
Q{
P(full)
从缓冲区取出
V(full)
消费产品
}
多个生产者—消费者问题
有多个生成者进程P1、P2、P3、P4
有多个消费者进程S1、S2、S3、S4
缓冲池k有多个缓冲区构成的圆环组成。
同步问题:
- 生产者不可往满了的缓冲区中存放商品,设置信号量empty,初始值为k,指示空闲缓冲区数量
- 消费者不可从空的缓冲区取商品,设置信号量full,初始值为0,指示满缓冲区数量
互斥问题:
- 设不能从空缓冲区区,亦不能向满缓冲区存,置信息量mutex,初始值为1,用于实现临界区(环形缓冲池)的互斥
P{
生产商品
P(empty)
P(mutex)
送入缓冲区
V(mutex)
V(full)
}
Q{
P(full)
P(mutex)
从缓冲区取出产品
V(empty)
V(empty)
消费产品
}
读者—写者问题
计算机系统中,一个数据对象是可供若干进程共享的。
问题描述:
- 多个进程可同时读取文件F
- 任何进程对文件进行写时,其他进程不可进行读或写
- 有进程读文件时不允许其他进程去写
设值read_count(0)记录当前读者的个数,是一个共享变量。
设置信号量mutex(1),用于read_count互斥使用,再设置信号量write(1)用于写者之间互斥。
读者:
{
// 互斥操作,同一时间只有一个进程可操作
P(mutex)
read_count = read_count + 1
// 只有当前读者一人,可进行写操作
if(read_count =1){
p(write)
}
V(mutex)
读文件
P(mutex)
read_count = read_count - 1
if(read_count =1){
v(write)
}
V(mutex)
}
写者
{
P(write)
写文件
V(write)
}
管程
管程的提出
采用信号量即P、V同步机制来编写并发程序,对于共享变量及信号量变量的操作将被分散于各个进程中,缺点如下:
- 程序易读性差
- 程序不利于修改和维护
- 正确性难以保证
为了更易于编写正确的程序,产生了另一种高级同步机制,即管程
管程的概念及组成
一个管程由过程、变量、数据结构组成的一个集合,他们组成一个特殊的模块。
进程可在任何需要的时候调用管程中的过程,但他们不能在管程之外声明的过程中直接访问管程内的数据结构。
管程由四部分组成:
- 管程名称
- 共享数据的说明
- 对数据进行操作的一组过程
- 共享数据赋初值的语句
管程可保证共享程序的互斥允许,即任意时刻只能有一个进程在管程内活动。该性能由管程本身实现的。
管程的三个主要特性:
- 模块化,一个管程是一个基本程序单位,可以单独编译。
- 抽象数据类型,是一种特殊的数据类型,不仅包含数据,还包含对数据操作的代码
- 信息隐蔽,外部无法得知管程内部功能的具体实现。管程内的共享变量在管程外是不可见的。
管程中的条件变量
条件变量:因为管程同时时间只可运行一个进程,所以需要一种办法来使其余进程被阻塞。
- wait:会使调用进程自身堵塞,并将另一个等待在管程外的进程调入管程。
- signal:可以唤醒正在睡眠的伙伴进程。
条件变量不是计数器,不管是否成功(比如并没有等待进程),信号都会永久消失。
进程通信
一个进程在运行过程中,可能会需要与其他进程进行信息交互。因此需要引入新的通信原语,解决大量信息交换问题。
解决大量信息通信的三种方案:
- 共享内存
- 消息机制
- 通过共享文件通信(管道通信)
不仅要保证相互制约的进程之间正确的关系,还要同时实现进程之间的信息交换。
共享内存
在相互通信的进程之间设置一个公共内存区,一组进程向该内存中写,另一组进程从公共内存中读,通过这种方式实现两组进程间的信息交换。
问题:
- 怎样提供共享内存
- 公共内存中的读写互斥问题
操作系统一般只提供要共享的内存空间,而处理进程间在公共内存的互斥关系有开发人员承担。
消息机制
消息机制是用于进程间通信的高级通信原语之一。至于具体的实现,是一种具体的消息传递机制。
消息缓冲通信
基本思想是:根据生产者-消费者原理,利用内存中公用消息缓冲区(临界区)实现进程间的信息交换。
消息缓冲机制包含下列内容:
- 消息缓冲区是一种数据结构
- 消息长度
- 消息正文
- 发送者
- 消息队列指针
- 消息队列首指针 m_q,一般存在PCB中
- 互斥信号量 m_mutex(1),用于互斥消息队列,在PCB中设置
- 同步信号量 m_syn(0),用于消息计数,在PCB中设置
- 发生消息原语send(receiver a)
- 接收消息原语receive(a)
信箱通信
为了实现进程间的通信,可设置一个信息机构:信箱,以发送信件以及接收回答信件为进程间通信的基本方式。
当一个进程希望与另一个进程通信时,就创建一个链接两个进程的信箱,发送进程把消息投入信箱,接收进程可以在任何时刻取走信件。
信箱由两部分组成
- 信箱说明
- 可存信件数
- 已有信件数
- 可存信件的指针
- 信箱体
为实现信箱通信,必须提供响应的原语。如创建信箱原语,撤销信箱原语,发送信件原语,读取信件原语等
管道通信
所谓管道通信就是连接两个进程之间的一个打开的共享文件,专用于进程之间通信。(类似于消息队列)
发送进程源源不断地从管道一端写入数据,接收进程在需要时从管道另一端读取数据,每次写入或读取的数据信息长度是可变的。显然,管道通信的基础是文件系统
管道机制中的同步与互斥机制都是由操作系统自动进行,对用户是透明的。
管道痛惜具有传送数据量大的优点,但是通信速度较慢。