操作系统-进程同步机制

进程同步机制

进程的并发性
并发运行的系统中,若干个作业可以同时运行,而每个作业又需要多个进程协作完成。而这些同时存在的进程间具有并发性,称之为“并发进程”

并发进程之间可能没有关系,也可能存在某种关系。如果进程彼此毫无关系,,互不影响,这时候不会对系统产生什么影响,通常不是研究的重点。如果进程间彼此相关,那么就需要合理的控制和协调才能正确地运行,

进程间的关系可以分为:

1、资源共享关系:系统中某些进程需要访问共同的资源,即当一个进程访问共享资源时,其他访问该共享资源的进程必须等待,当前进程使用结束后,其他进程差才能使用,这时需要互斥地访问共享资源。
2、相互合作关系:系统中某些进程之间存在相互合作的关系,即当一个进程执行完成后,另一个进程才能开始,否则另外一个进程不能开始,这时就要保证相互合作的进程在执行次序上要同步。
进程同步的概念
临界资源:在系统中有许多硬件和软件资源,如打印机、公共变量等,这些资源在一段时间内只允许一个进程访问或者使用,这种资源称之为临界资源。
临界区:作为临界资源,不论硬件临界资源还是软件临界资源,多个并发的进程都必须互斥地访问或者使用,这时候,把每个进程访问临界资源的那段代码称为临界区。
进程同步:进程同步是指多个相关进程在执行次序上的协调这些进程相互合作,在一些关键点上需要相互等待或者通信。通过临界区可以协调进程间的合作关系,这就是同步。
进程互斥:进程互斥是指当一个程序进入临界区使用临界资源时,另一个进程必须等待。当占用临界资源的进程退出临界区后,另一个进程才被允许使用临界资源。通过临界区可以协调程序间资源共享关系,就是进程互斥。进程互斥是同步的一种特例。

进程同步机制遵循的原则

空闲让进:当无进程处于临界区时,临界区处于空闲状态,可以允许一个请求进入临界区的进程进入临界区,有效地使用临界资源。
忙则等待:当有进程进入自己的临界区时,意味着临界资源正在被访问,因而其他的试图进入临界区的进程必须等待,以保证进程互斥地使用临界资源。
有限等待:对要求访问临界资源的进程,必须保证该进程在有效的时间内进入自己的临界区,以免出现死等的情况。
让权等待:当进程不能进入自己的临界区时,应该立即释放处理器,以免陷入“忙等”
综上,当有若干个进程同时进入自己的临界区时,应该在有限时间内使进程进入临界区。它们不能因相互等待而使彼此不能进入临界区。但是每次至多有一个进程进入临界区,并且在临界区只能停留有限的时间。

进程同步机制——锁
在同步机制中,常用一个变量来代表临界资源的状态,称之为锁。通常用“0”代表资源可用,相当于锁打开,用“1”代表资源已被占用,相当于锁闭合。

对锁的操作有两种:一种是关锁操作,一种是开锁操作
在这里插入图片描述

//关锁
lock(w)
{
test:if(w==1) go to test;
else w==1;
}
 
//开锁
unlock(w)
{
w=0;
}

进程同步机制——信号量

信号量是一种特殊的变量,用来表示系统中资源的使用情况。整型信号量就是一个整型变量。当其值大于0时,表示系统中对应 可用资源的数目。当其值小于0时,其绝对值表示因该资源而处在等待状态的进程的数目。当其值等于0,表示系统中对应资源已用完,并且没有因该资源处在等待状态的进程。

对信号量的操作

PV操作

p操作(wait):申请一个单位资源,在进程进入临界区前执行;v操作(signal):释放一个单位资源,在进程退出临界区之后执行。

//经典伪代
 
P(S){  
  S=S-1;
  if(S<0W(S)//  W(S)将进程插入到信号量的等待队列中
}  
 
 
V(S){  
    S=S+1;  
    if(S<=0R(S)//  R(S)从该信号量的等待队列中移出一个进程
} 

PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。

三个经典同步问题

a.生产者-消费者(缓冲区问题)

产者一消费者问题(producer-consumerproblem)是指若干进程通过有限的共享缓冲区交换数据时的缓冲区资源使用问题。假设“生产者”进程不断向共享缓冲区写人数据(即生产数据),而“消费者”进程不断从共享缓冲区读出数据(即消费数据);共享缓冲区共有n个;任何时刻只能有一个进程可对共享缓冲区进行操作。所有生产者和消费者之间要协调,以完成对共享缓冲区的操作。

/*生产者进程结构:*/
do{  
     wait(empty) ;  
     wait(mutex) ;  
      
     add nextp to buffer  
      
     signal(mutex) ;  
     signal(full) ;  
}while(1) ;
 
/*消费者进程结构:*/
do{  
     wait(full) ;  
     wait(mutex) ;  
      
     remove an item from buffer to nextp  
      
     signal(mutex) ;  
     signal(empty) ;  
}while(1) ;

我们可把共享缓冲区中的n个缓冲块视为共享资源,生产者写人数据的缓冲块成为消费者可用资源,而消费者读出数据后的缓冲块成为生产者的可用资源。为此,可设置三个信号量:full、empty和mutex。其中:full表示有数据的缓冲块数目,初值是0;empty表示空的缓冲块数初值是n;mutex用于访问缓冲区时的互斥,初值是1。实际上,full和empty间存在如下关系:full + empty = N

注意:这里每个进程中各个P操作的次序是重要的。各进程必须先检查自己对应的资源数在确信有可用资源后再申请对整个缓冲区的互斥操作;否则,先申请对整个缓冲区的互斥操后申请自己对应的缓冲块资源,就可能死锁。出现死锁的条件是,申请到对整个缓冲区的互斥操作后,才发现自己对应的缓冲块资源,这时已不可能放弃对整个缓冲区的占用。如果采用AND信号量集,相应的进入区和退出区都很简单。如生产者的进入区为Swait(empty,mutex),退出区为Ssignal(full,mutex)。

b.作者读者问题
读者一写者问题(readers-writersproblem)是指多个进程对一个共享资源进行读写操作的问题。

假设“读者”进程可对共享资源进行读操作,“写者”进程可对共享资源进行写操作;任一时刻“写者”最多只允许一个,而“读者”则允许多个。即对共享资源的读写操作限制关系包括:“读—写,互斥、“写一写”互斥和“读—读”允许。

我们可认为写者之间、写者与第一个读者之间要对共享资源进行互斥访问,而后续读者不需要互斥访问。为此,可设置两个信号量Wmutex、Rmutex和一个公共变量Rcount。其中:Wmutex表示“允许写”,初值是1;公共变量Rcount表示“正在读”的进程数,初值是0;Rmutex表示对Rcount的互斥操作,初值是1。

在这个例子中,我们可见到临界资源访问过程的嵌套使用。在读者算法中,进入区和退出区又分别嵌套了一个临界资源访问过程。

对读者一写者问题,也可采用一般“信号量集”机制来实现。如果我们在前面的读写操作限制上再加一个限制条件:同时读的“读者”最多R个。这时,可设置两个信号量Wmutex和Rcount。其中:Wmutex表示“允许写”,初值是¨Rcount表示“允许读者数目”,初值为R。为采用一般“信号量集”机制来实现的读者一写者算法。

c.哲学家进餐问题

(1) 在什么情况下5 个哲学家全部吃不上饭?
考虑两种实现的方式,如下:

//算法描述
void philosopher(int i) /*i:哲学家编号,从0 到4*/   
{   
    while (TRUE) {   
        think( ); /*哲学家正在思考*/   
        take_fork(i); /*取左侧的筷子*/   
        take_fork((i+1) % N); /*取左侧筷子;%为取模运算*/   
        eat( ); /*吃饭*/   
        put_fork(i); /*把左侧筷子放回桌子*/   
        put_fork((i+1) % N); /*把右侧筷子放回桌子*/   
    }   
}

分析:假如所有的哲学家都同时拿起左侧筷子,看到右侧筷子不可用,又都放下左侧筷子,
等一会儿,又同时拿起左侧筷子,如此这般,永远重复。对于这种情况,即所有的程序都在
无限期地运行,但是都无法取得任何进展,即出现饥饿,所有哲学家都吃不上饭。

B.
算法描述:
规定在拿到左侧的筷子后,先检查右面的筷子是否可用。如果不可用,则先放下左侧筷子,
等一段时间再重复整个过程。
分析:当出现以下情形,在某一个瞬间,所有的哲学家都同时启动这个算法,拿起左侧的筷
子,而看到右侧筷子不可用,又都放下左侧筷子,等一会儿,又同时拿起左侧筷子……如此
这样永远重复下去。对于这种情况,所有的程序都在运行,但却无法取得进展,即出现饥饿,
所有的哲学家都吃不上饭。
(2) 描述一种没有人饿死(永远拿不到筷子)算法。
考虑了四种实现的方式(A、B、C、D):
A.原理:至多只允许四个哲学家同时进餐,以保证至少有一个哲学家能够进餐,最终总会释
放出他所使用过的两支筷子,从而可使更多的哲学家进餐。以下将room 作为信号量,只允
许4 个哲学家同时进入餐厅就餐,这样就能保证至少有一个哲学家可以就餐,而申请进入
餐厅的哲学家进入room 的等待队列,根据FIFO 的原则,总会进入到餐厅就餐,因此不会
出现饿死和死锁的现象。

semaphore chopstick[5]={11111};  
semaphore room=4;   
void philosopher(int i)   
{   
    while(true)   
    {   
        think();   
        wait(room); //请求进入房间进餐   
        wait(chopstick[i]); //请求左手边的筷子   
        wait(chopstick[(i+1)%5]); //请求右手边的筷子   
        eat();   
        signal(chopstick[(i+1)%5]); //释放右手边的筷子   
        signal(chopstick[i]); //释放左手边的筷子   
        signal(room); //退出房间释放信号量room   
    }   
}

B.原理:仅当哲学家的左右两支筷子都可用时,才允许他拿起筷子进餐。
方法1:利用AND 型信号量机制实现:根据课程讲述,在一个原语中,将一段代码同时需
要的多个临界资源,要么全部分配给它,要么一个都不分配,因此不会出现死锁的情形。当
某些资源不够时阻塞调用进程;由于等待队列的存在,使得对资源的请求满足FIFO 的要求,
因此不会出现饥饿的情形。

semaphore chopstick[5]={11111};   
void philosopher(int I)   
{   
    while(true)   
    {   
        think();   
        Swait(chopstick[(I+1)]%5,chopstick[I]);   
        eat();   
        Ssignal(chopstick[(I+1)]%5,chopstick[I]);   
    }   
}

方法2:利用信号量的保护机制实现。通过信号量mutex对eat()之前的取左侧和右侧筷
子的操作进行保护,使之成为一个原子操作,这样可以防止死锁的出现。
伪码:

semaphore mutex = 1 ;   
semaphore chopstick[5]={11111};   
void philosopher(int I)   
{   
    while(true)   
    {   
        think();   
        wait(mutex);   
        wait(chopstick[(I+1)]%5);   
        wait(chopstick[I]);   
        signal(mutex);   
        eat();   
        signal(chopstick[(I+1)]%5);   
        signal(chopstick[I]);   
    }   
}
 
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值