操作系统进程同步

操作系统进程同步

一.进程同步的基本概念

进程同步机制的主要任务是对多个相关进程在执行次序上进行协调,使并发执行的多个进程之间按照一定的规则共享系统资源,并能很好地相互合作,从而使程序的执行具有可再现性。

1.两种形式的制约关系

(1)间接相互制约关系
由于共享系统资源,如CPU,IO设备等而致使这些并发执行的程序之间形成的相互制约关系。

(2)直接相互制约关系
由于进程之间相互合作而产生的制约关系,例如输入进程A和计算进程B,共享一个缓冲区,输入进程A向缓冲区输入数据,当缓冲区为空时,计算进程B由于不能获得所需数据而被阻塞。

2.临界资源(critical resource)

多个进程需要互斥访问的共享资源,如打印机,磁带机等。

3.临界区(critical section)

每个进程中访问临界资源的那段代码称为临界区。若能保证多个进程互斥地进入自己的临界区,就可以实现对临界资源的互斥访问。
while(true)
{
进入区 :用于检查欲访问的临界资源是否正在被其他进程访问
临界区:访问临界资源,将临界区设置为正在被访问的标志
退出区:将临界区正在被访问的标志恢复为未被访问的标志
剩余区:其他部分代码
}

4.同步进制遵循的原则

  • 空闲让进:当无进程处于临界区时,表明临界资源处于空闲状态,应允许一个请求进入临界区的进程立刻进入自己的临界区,以有效利用临界资源。
  • 忙则等待:当已有进程进入临界区时,表明临界资源正在被访问,因而其他试图进入临界区的进程必须等待,以保证对临界资源的互斥访问。
  • 有限等待:对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,以免陷入”死等“状态。
  • 让权等待:当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入”忙等“状态。

二.硬件同步机制

1.关中断

在进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断。进程在临界区执行期间,计算机系统不响应中断,从而不会引发调度,也就不会发生进程或线程的切换。由此保证了对锁测试和关锁操作的连续性和完整性,有效保证了互斥。

关中断的缺陷
①滥用关中断权力可能导致严重后果。
②关中断时间过长影响系统效率,限制了处理器交叉执行程序的能力。
③关中断方法不适用于多CPU系统,在一个处理器上关中断不能阻止进程在其他处理器上执行相同的临界段代码。

2.Test-and-Set 指令

借助硬件指令——“测试并建立”指令TS(Test-and-Set)以实现互斥的方法。

  • 这条指令可以看作为一个函数过程,其执行过程是不可分割的,是一条原语。
  • lock有两种状态:*lock=FALSE时,表示该资源空闲;当 *lock=TRUE时,表示该资源正在被使用。
boolean TS(boolean *lock)
{
   boolean old;
   old=*lock;
   *lock=TRUE;
   return old;
}

使用TS指令管理临界区时,为每个临界资源设置一个布尔变量lock,lock可以看成是临界资源的一把锁,只有当lock的值为FALSE时该临界资源才能被访问,并且lock的初值也是FALSE。
该TS函数的执行过程可以描述为:

  • 将锁的状态*lock复制到变量old中,变量old代表原来锁的状态
  • 将锁lock赋值为TRUE,等效于将锁关闭,使临界资源不能被访问
  • 返回保存了锁原本状态的布尔变量old

利用TS指令实现互斥的循环进程结构可描述如下:

do{
  ...
  while(TS(&lock));
  critical section;//临界区
  lock=FALSE;      //退出区
  remainder section;//剩余区
}
  • 首先在进入临界区之前,需要不断用TS指令来测试锁的状态,只有当测试结果为锁开,即lock=FALSE时,才能进去临界区
  • 在执行完临界区程序后,需要将锁打开,即将lock赋值FALSE,以便别的进程正常访问临界资源

3.Swap指令实现进程互斥

该指令称为对换指令,再Intel 80x86中称为XCHG指令,用于交换两个字的内容

void swap(boolean *a,boolean *b)
{
  boolean temp;
  temp=*a;
  *a=*b;
  *b=temp;
}

对换指令可以简单有效地实现互斥,方法是为每一个临界资源设置一个全局布尔变量lock,初始值为FALSE,在每个进程中再利用一个局部布尔变量key,利用交换lock和key的值来检测锁的状态并保证锁的关闭。

利用swap指令实现进程互斥的循环进程可描述如下:

do{
  key=TRUE;
  do{
     swap(&lock,&key);
    }while(key!=FALSE);
    临界区操作;
    lock=FALSE;
    ...
  }while(TRUE);
  • 首先将变量key赋值为TRUE,即可以让锁关闭的状态
  • 之后循环交换lock和key的值,将lock锁的状态交换到key并同时将key的值TRUE交换给锁,即每一次交换之后锁的状态始终是关闭的,防止其他进程中途访问临界资源。
  • 只有当从key得到锁的状态是FALSE,即锁被打开的状态时,才允许进行下面的临界区操作。
  • 当临界区操作执行完毕后,再将lock锁赋值为FALSE,即打开锁来让其他进程正常访问

三.信号量机制

1.整型信号量

最初有Dijkstra把整型信号量定义为一个用于表示资源数目的整型量S,它与一般整型量不同,除初始化外,仅能通过两个标准原子操作(Atomic Operation):wait(s),signal(s) 访问,很长时间以来,这两个操作一直被分别称为P,V操作。

Wait(S)
{
    while(S<=0);
    S--;
 }
signal(S)
{
   S++;
}
  • wait(S)和signal(S)是两个原子操作,在执行时是不可中断的,当某个进程在修改某信号量S时,没有其他进程可以同时对该信号量进行修改
  • 在wait(S)中,对S值的测试和S - -的操作都不可中断
  • wait(S)操作中,只要信号量S<=0就会不断进行测试,所以该机制并未遵循“让权等待”的准则,而是使进程处于“忙等”状态。

2.记录型信号量

记录型信号量机制中S->value的初值表示系统中某类资源的数目,因而又称为资源信号量
记录型信号量采取了“让权等待”策略而不存在“忙等”现象
但是会有多个进程等待访问同一临界资源的情况
所以在记录型信号量机制中除了需要一个表示资源数目的变量value外,还需要增加一个进程链表指针list,用于链接上述所有等待的进程。
记录型信号量是由于它采用了记录型的数据结构而得名的。

信号量semaphore变量的数据结构可描述如下:

typedef struct{
    int value;
    struct process_control_block *list;
    }semaphore;

记录型信号量机制下的wait操作

wait(semaphore *S){
    S->value--;
    if(S->value<0) block(S->list);
    }
  • 每次wait操作意味着该进程请求一个单位的该类资源,使系统中可供分配的该类资源数目减少一个
  • 当S->value<0时,表示该类资源已无可分配,因此进程调用block原语进行自我阻塞,放弃处理机,并插入到信号量链表S->list中。

记录型信号量机制下的signal操作

signal(semaphore *S){
    S->value++;
    if(S->value<=0) wakeup(S->list);
    }
  • 每次signal操作表示执行进程释放一个单位资源,使系统中可供分配的该类资源数目增加一个
  • 若加1后仍有S->value<=0,则表示该信号量链表中仍有等待该资源的进程被阻塞,故应该调用wakeup原语,将S->list链表中的第一个等待进程唤醒。
  • 若S->value的初值为1,则表示只允许一个进程访问临界资源,此时信号量转化为互斥信号量,用于进程互斥。

3.AND型信号量

AND型信号量的引入原因:
AND型信号量针对一个进程需要获得两个或更多的共享资源后方能执行任务的场合。
假定两个进程A和B,它们都要求访问共享数据D和E,共享数据都应作为临界资源。为此,可为这两个数据分别设置用于互斥的信号量Dmutex和Emutex,并令它们的初值都为1。相应的,在两个进程中都应包含对Dmutex和Emutex的操作,即:

Process A:
wait(Dmutex);
wait(Emutex);

Process B:
wait(Dmutex);
wait(Emutex);

若进程A和B按下述次序交替执行wait操作:

process A: wait(Dmutex); //于是Dmutex=0
process B: wait(Emutex); //于是Emutex=0
process A: wait(Emutex); //于是Emutex=-1 进程A阻塞
process B: wait(Dmutex); //于是Dmutex=-1 进程B阻塞

此时进程A和B进入死锁状态,当进程同步时要求的共享资源越多时,发生死锁的可能性就越大。

AND型信号量的思想:
AND同步机制的基本思想是,将进程在整个运行过程中所需的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。只要有一个所需资源未能分配给该进程,其他可以分配给该进程的资源也不对该进程进行分配。
即对若干临界资源的分配采取原子操作,要么全部分配要么一个也不分配。

AND型信号量进制的实现
Swait操作:

Swait(S1,S2,...,Sn)
{
    while(TRUE)
    {
      if(Si>=1&&...&&Sn>=1)
      {
       for(i=1;i<=n;i++) Si--;
       break;
      }
      else{
      place the process in the waiting queue associated with the first Si found with Si<1,and set the program count of this process to the beginning of Swait operation
      将进程放入与Si<1的第一个Si相关联的等待队列中,并将此进程的程序计数设置为Swait操作的开始 
      }
    
   }
}

Ssignal操作:

Ssignal(S1,S2,...,Sn){
    while(TRUE)
    {
      for(i=1;i<=n;i++)
      {
        Si++;
        Remove all the process waiting in the queue associated with Si into the ready queue
        将与Si相关联的队列中等待的所有进程移到就绪队列中
      }
    }
}

4.信号量集

  • 对AND型信号量机制进行扩充,对进程所申请的所有资源以及每类资源不同的资源需求量,在一次P,V原语操作中完成申请或释放。
  • 进程对信号量的测试值不再是1,而是该类资源的分配下限值ti,即要求Si>=ti,否则不进行分配。
  • 当允许分配时,进程对该类资源的需求值为di,即表示资源占用量,进行Si=Si-di操作,而不是Si=Si-1.
  Swait(S1,t1,d1,...,Sn,tn,dn); 
  Ssignal(S1,d1,...,Sn,dn);

四.信号量的应用

1.进程互斥

semaphore mutex = 1; // mutex 取值为 -1,0,1
  • mutex=1时表示两个进程皆未进入需要互斥的临界区
  • mutex=0时表示有一个进程进入临界区运行,另外一个进程必须等待,挂入阻塞队列
  • mutex=-1时表示有一个进程正在临界区运行,另一个进程因等待而阻塞在信号量队列中,需要被当前已在临界区运行的进程退出时唤醒。
PA()
{
while(1)
{
wait(mutex);
  临界区;
signal(mutex);
  剩余区;
}
}
PB()
{
while(1)
{
wait(mutex);
  临界区;
signal(mutex);
  剩余区;
}
}
  • 在利用信号量机制实现进程互斥时应该注意,wait(mutex)和signal(mutex)应该成对出现。
  • 缺少wait(mutex)将会导致系统混乱,不能保证对临界资源的互斥访问
  • 缺少signal(mutex)将会使临界资源永远不被释放,从而使等待该资源而阻塞的进程不能被唤醒。

2.前趋关系

假设有两个进程P1和P2。P1中有语句S1,P2中有语句S2.希望S2在S1之后执行。
为实现上述前驱关系,需要使进程P1和P2共享一个公用信号量S,并赋初值为0,将signal(S)操作放在语句S1后面,并在S2语句前面插入wait(S)操作。

P1中:

S1;
signal(S);

P2中:

wait(S);
S2;
  • 初始状态下S=0,P2先执行会阻塞
  • 只有当P1执行完后,通过wait(S)原语使S+1后S的值为1时,P2才能执行S2语句

利用信号量机制实现如下较复杂的前趋图:

在这里插入图片描述

p1(){S1;signal(a);signal(b)}
p2(){wait(a);S2;signal(c);signal(d);}
p3(){wait(b);S3;signal(e);}
p4(){wait(c);S4;signal(f);}
p5(){wait(d);S5;signal(g);}
p6(){wait(e);wait(f);wait(g);S6;}
main()
{
  semaphore a,b,c,d,e,f,g;
  a.value=b.value=c.value=d.value=e.value=f.value=g.value=0;
  cobegin
    p1();p2();p3();p4();p5();p6();
  coend
}

五.管程机制

信号量机制的缺点:(管程的引入) 在信号量机制中,每个要访问临界资源的进程都必须自备同步操作wait(S)和signal(S)。这就使大量的同步操作分散在各个进程中。不仅给系统的管理带来了麻烦,而且还会因同步操作的使用不当而导致系统死锁。

管程的定义: 代表共享资源和数据结构以及由对该共享数据结构实施操作的一组过程所组成的资源管理程序共同构成了一个操作系统的资源管理模块,我们称之为管程。管程被请求和释放资源的进程所调用。Hansan为管程所下的定义是:“一个管程定义了一个数据结构和能为并发进程所执行在该数据结构上的一组操作,这组操作能同步进程和改变管程中的数据。”
管程由四部分组成:

  • ①管程的名称
  • ②局部于管程的共享数据结构说明
  • ③对该数据结构进行操作的一组过程
  • ④对局部与管程的共享数据设置初始值的语句

在这里插入图片描述

⭐管程的特点:

  • 管程包含了面向对象的思想,它将表征共享资源的数据结构及其对数据结构操作的一组过程,包括同步机制,都集中并封装在一个对象内部,隐藏了实现细节。

  • 封装于管程内部的数据结构仅能被封装于管程内部的过程所访问,任何管程外的过程都不能访问它;反之,封装于管程内部的过程只能访问管程内部的数据结构。

  • 所有进程访问临界资源时,都只能通过管程间接访问,而管程每次只准许一个进程进入管程,执行管程内的过程,从而实现进程互斥。

    概括来讲,管程的主要特性有:

    • ①模块化:是基本程序单位,可单独编译
    • ②抽象数据类型:即包含数据,也包含对数据的操作
    • ③信息掩蔽:管程内数据只能被管程内过程访问,内部数据结构和过程对外不可见

⭐条件变量

为什么需要条件变量(条件变量的引入): 当一个进程调用了管程,在管程中被阻塞或挂起,直到阻塞或挂起的原因接触,在此期间如果该进程不释放管程,则别的进程无法进入管程而被迫长时间等待。为了解决这个问题,引入了条件变量condition。

条件变量的定义:

  • 由于进程被阻塞或挂起的原因可有多个,所以设置多个条件变量,对这些条件变量的访问只能在管程中进行。管程中每个条件变量都用condition x,y的形式进行说明
  • 每个条件变量保存了一个链表,用于记录因该条件变量而阻塞的所有进程
  • 条件变量是抽象数据类型,对条件变量的操作仅仅是wait和signal。
    • x.wait:正在调用冠层的进程因x条件需要被阻塞或挂起,则调用x.wait将自己插入到x条件的等待队列上,并释放管程,直到x条件发生变化。
    • x.signal:正在调用管程的进程发现x条件发生了变化,则调用x.signal,重新启动一个因x条件而阻塞或挂起的进程,如果存在多个这样的进程,则选择其中一个,如果没有,继续执行原进程,而不产生任何结果。(这与信号量机制中的signal操作不同,因为后者总是要执行s=s+1操作,因而总会改变信号量的状态)

管程的语法描述如下:

Monitor monitor_name{ //管程名

share variable declarations; //共享变量说明

cond declarations;           //条件变量说明

public:                      //能被进程调用的过程

void P1(......)              //对数据结构操作的过程
{......}
void P2(......)
{......}
......
  void(......)
  {......}
  ......

  {                         //管程主体
  initalization code;       //初始化代码
  ......
  }
}



}

管程与进程的联系和区别:

  • ①虽然二者都定义了数据结构,但进程定义的是私有数据结构PCB,管程定义的是公共数据结构,如消息队列等;
  • ②二者都存在对各自数据结构上的操作,但进程是由顺序程序执行有关操作,而管程主要是进行同步操作和初始化操作;
  • ③设置进程的目的在于实现系统的并发性,而管程的设置则是解决共享资源的互斥使用问题;
  • ④进程通过调用管程中的过程对共享数据结构实行操作,该过程就如通常的子程序一样被调用,因而管程为被动工作方式,进程为主动工作方式。
  • ⑤进程之间能并发执行,而管程则不能与其调用者并发;
  • ⑥进程具有动态性,由“创建”而诞生,由“撤销”而消亡,而管程则是操作系统中的一个资源模块(静态),供进程调用。
  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值