在多道程序系统中,由于进程并发,资源共享与进程协作,使得进程间可能产生两种形式的制约:
1、间接相互制约:源于资源共享,如果进程ab共享一种资源,如果a请求资源时发现进程b正在访问这种资源,那么进程a就进入阻塞队列,当进程b释放资源,进程a被唤醒到就绪状态
2、直接相互制约:源于进程协作,如果进程a通过单项缓冲向进程b提供数据,当缓冲为空进程b得不到数据而进入阻塞队列,一旦a进程的数据进入缓冲,进程b被唤醒,反之,缓冲满了,则a被阻塞,只有b取走数据后a才能被唤醒
进程同步:同步进程的直接相互制约
进程互斥:源于间接相互制约,资源共享,保证每次只有一个进程访问共享资源
临界资源:一次只允许一个进程访问的资源
临界区:访问临界资源的那段程序
同步准则:空闲就进,遇忙则等,有限等待(对要求进入的进程,应在有限的时间内进入,避免死等),让权等待(对于等待进程要立即释放处理机,避免进程忙等)
实现临界区互斥的基本方法:
1、单标志位:
进程1:
while(turn!=0);
do_something
turn = 1;
进程2
while(turn!=1);
do_something
turn = 0;
算法设置了一个标志位,0时候允许进程1进入临界区,1时候运行进程2进入临界区,弊端:了,两个进程必须交替进入临界区
2、双标志位先检查:
while(flag[j]);1
flag[i] = true;3
do_something
flag[i] = false;
进程2
while(flag[i]);2
flag[j] = true;4
do_something
flag[j] = false;
优点可以连续使用,确定如果按照1234的顺序执行,可能两个进程同时进入临界区
2、双标志位后检查:
flag[i] = true;
while(flag[j]);
do_something
flag[i] = false;
进程2
flag[j] = true;
while(flag[i]);
do_something
flag[j] = false;
先置位后检查,可以克服上个算法的缺点,但是有可能两个进程先后置位后双方都不能进入临界区
4、peterson algorithm
flag[i] = true;
turn = j;
while(flag[j]&&turn == j);
do_something
flag[i] = false;
进程2
flag[j] = true;
turn = i;
while(flag[i]&&turn == i);
do_something
flag[j] = false;
增加一个标志位turn表示不允许某进程进入临界区,可以使得只有一个进程进入临界区
完全用软件方式实现同步和互斥机制,有很大的局限性,用硬件支持的原子性指令(如ts指令和swap指令),可以使操作不会打断,可以解决临界区问题
1、检测和设置ts指令
一个bool值false的时候没有进程使用,true的时候有进程使用
while TS(&lock);
do_something
lock = false;
2、swap指令
key = true;
do
{
swap(&lock,&key);
}while(&key);
do_something
lock = false;
硬件方法的有点:适用于任意数目的进程,简单容易验证正确性,如果有多个临界区,只要为每个临界区设立一个bool变量
缺点:等待要耗费处理机时间,不嫩做到让权等待,可能出现饥饿,可能出现死锁
信号量
之前提到的算法都是平等进程间的协商机制,操作系统可以从进程管理者的角度来处理互斥问题,信号量就是一种有效手段
信号量是一个荷兰科学家1985年提出,p,v都是荷兰语的首字母,由一个二元组组成,一个int初始非负,一个初始状态为空的等待队列(pcb队列)int大于零表示资源个数,小于零表示排队进程数,p为减一,v为加一。
p操作:
int--;申请一个资源
if(int<0)表示没有空闲资源
{
进程进入等待队列
阻塞进程
}
v操作
int++;释放一个资源
if(int<=0)表示有进程处于阻塞状态
{
从等待队列取出一个进程
进程进入就绪队列,唤醒一个进程分配处理机
}
互斥模型实现:
typedef int semaphore;
semaphore mutex = 1;
p1
{
p(mutex);
do_something
v(mutex);
}
p2
{
p(mutex);
do_something
v(mutex);
}
同步模型(p1生产,p2消费)
typedef int semaphore;
semaphore mutex = 0;
p1
{
do_something
v(mutex);
}
p2
{
p(mutex);
do_something
}
管程:用信号量和pv操作,信号量的操作分散在进程中,易读性,维护性,正确性难以保证
管程定义:共享资源用数据结构表示时候,资源管理程序可以定义一组过程处理资源,把这组过程和数据结构叫做管程,3部分共享数据,过程方法和初始化,任何时候只有一个进程可以在管程中运行,管程需要编译器支持
经典的同步互斥问题:
死锁:两个以上的进程无限期的等待不会发生的条件,处于一种停滞状态,产生死锁的原因:推进顺序不当,对互斥资源分配不当
四个必要条件:
互斥条件(一次只能有一个进程使用资源),占用并请求(部分占用,还要请求更多资源),非剥夺条件,循环等待(请求资源的进程形成了循环)
处理策略:
1、忽略:鸵鸟算法
2、死锁检测和恢复:根据保有的资源信息定期检查是否有死锁(有向图法和矩阵法),然后处理1、资源剥夺(挂起进程释放资源)2、进程撤销(撤销占用资源多或者代价小的进程),进程回退(如果有进程历史信息,可以回退到无死锁状态),重启系统。
3、死锁避免:银行家算法,规定安全顺序和非安全顺序,安全一定不会死锁,不安全可能有死锁
4、预防死锁:打破四个必要条件
由于锁获取顺序产生的死锁:
thread1:
a.clock()
b.clock()
thread2
b.clock()
a.clock()
顺序不同造成死锁,处理方法1改变顺序
2一次获得两个锁
用getlock(a,b)伪代码一次获得两个锁
在windows里面有WaitForMultipleObjects系统调用
线程之间的关系有三种:无关系,共享,同步。
进程之间通信、协调、通过一些事件通知或等待互斥锁的释放方面,不同的平台所支持的方式不一样
多进程共享数据代价大于线程,因为涉及序列化与反序列化的开销。一些ipc手段是单机os支持的,到了多机的分布式系统上要自己实现,而且性能有问题,网线带宽问题