操作系统之进程同步

操作系统之进程同步

一、 进程同步的基本概念:

  • 主要任务:进程同步机制的主要任务,是对多个相关进程在执行次序上进行协调,使并发执行的诸进程之间能按照一定的规则(或时序)共享系统资源,并能很好地相互合作,从而使程序的执行具有可再现性。
  • 两种形式的制约关系:
  1. 间接相互制约关系
    多个程序在并发执行时,由于共享系统资源,如CPU、I/O设备等,致使在这些并发执行的程序之间形成相互制约的关系。对于像打印机、磁带机这样的临界资源,必须保证多个进程对之只能互斥地访问,由此,在这些进程间形成了源于对该类资源共享的所谓间接相互制约关系。为了保证这些进程能有序地运行,对于系统中的这类资源,必须由系统实施统一分配,即用户在要使用之前,应先提岀申请,而不允许用户进程直接使用。
  2. 直接相互制约关系
    某些应用程序,为了完成某任务而建立了两个或多个进程。这些进程将为完成同一项任务而相互合作。进程间的直接制约关系就是源于它们之间的相互合作。例如,有两个相互合作的进程——输入进程A和计算进程B,它们之间共享一个缓冲区。进程A通过缓冲向进程B提供数据。进程B从缓冲中取出数据,并对数据进行处理。但如果该缓冲空时,计算进程因不能获得所需数据而被阻塞。一旦进程A把数据输入缓冲区后便将进程B唤醒; 反之,当缓冲区已满时,进程A因不能再向缓冲区投放数据而被阻塞,当进程B将缓冲区数据取走后便可唤醒A。
  • 临界区(critical section): 人们把在每个进程中访问临界资源的那段代码称为临界区(critical section)
  • 进入区(entry section): 在临界区前面增加一段用于进行上述检查的代码,把这段代码称为进入区(entry section)
  • 退出区(exit section): 在临界区后面也要加上一段称为退出区(exit section)的代码,用于将临界区正被访问的标志恢复为未被访问的标志。
  • 剩余区: 进程中除上述进入区、临界区及退出区之外的其它部分的代码在这里都称为剩余区。

二、所有同步机制都应遵循下述四条准则:

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

三、硬件同步机制

  1. 背景:虽然可以利用软件方法解决诸进程互斥进入临界区的问题,但有一定难度,并且存在很大的局限性,因而现在已很少采用。相应地,目前许多计算机已提供了一些特殊的硬件指令,允许对一个字中的内容进行检测和修正,或者是对两个字的内容进行交换等。可利用这些特殊的指令来解决临界区问题。
  2. 实现互斥的方法:
    • 关中断:
      关中断是实现互斥的最简单的方法之一。
      在进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断。这样,进程在临界区执行期间,计算机系统不响应中断,从而不会引发调度,也就不会发生进程或线程切换。由此,保证了对锁的测试和关锁操作的 连续性和完整性,有效地保证了互斥。但是,关中断的方法存在许多缺点:①滥用关中断 权力可能导致严重后果;②关中断时间过长,会影响系统效率,限制了处理器交叉执行程 序的能力;③关中断方法也不适用于多CPU系统,因为在一个处理器上关中断并不能防 止进程在其它处理器上执行相同的临界段代码。
    • 利用Test-and-Set指令实现互斥:
      这是一种借助一条硬件指令——“测试并建立”指令TS(Test-and-Set)以实现互斥的方法。在许多计算机中都提供了这种指令。
      TS指令的一般性描述如下:
      在这里插入图片描述
      见链接:https://blog.csdn.net/Abit_Go/article/details/104012860
      操作系统中的Test-and-Set指令实现互斥(java实现)
    • 利用Swap指令实现进程互斥
      该指令称为对换指令,在Intel 80x86中又称为XCHG指令,用于交换两个字的内容。 其处理过程描述如下:
      在这里插入图片描述
      用对换指令可以简单有效地实现互斥,方法是为每个临界资源设置一个全局的布尔变量lock,其初值为false,在每个进程中再利用一个局部布尔变量key。利用Swap指令实现进程互斥的循环进程可描述如下:
      在这里插入图片描述

四、信号量机制

  1. 整型信号量
    最初由Dijkstra把整型信号量定义为一个用于表示资源数目的整型量S,它与一般整型量不同,除初始化外,仅能通过两个标准的原子操作(Atomic Operation) wait(S)和signal(S)来访问。很长时间以来,这两个操作一直被分别称为P、V操作。wait和signal操作可描述如下:
    在这里插入图片描述
  2. 记录型信号量
    在整型信号量机制中的wait操作,只要是信号量S≤0,就会不断地测试。因此,该机制并未遵循“让权等待”的准则,而是使进程处于 “忙等” 的状态。记录型信号量机制则是一种不存在“忙等”现象的进程同步机制。但在釆取了 “让权等待”的策略后,又会出现多个进程等待访问同一临界资源的情况。为此,在信号量机制中,除了需要一个用于代表资源数目的整型变量value外,还应 增加一个进程链表指针list,用于链接上述的所有等待进程 。记录型信号量是由于它釆用了记录型的数据结构而得名的。它所包含的上述两个数据项可描述如下:
typedef struct {
int value;
struct process_control_block *list;
}semaphore;
  • 相应地,wait(S)和signal(S)操作可描述如下:
wait(semaphore *S){
S->value--;
if(S->value<0)block(S->list);
}
signal(semaphore *S){
S->value++;
if(S->value<=0)wakeup(S->list);
}
  • 在记录型信号量机制中,S-> value的初值表示系统中某类资源的数目,因而又称为资源信号量,对它的每次wait操作,意味着进程请求一个单位的该类资源,使系统中可供分配的该类资源数减少一个,因此描述为S-> value–;当S->value < 0时,表示该类资源已分配完毕,因此进程应调用block原语进行自我阻塞,放弃处理机,并插入到信号量链表S-> list中。可见,该机制遵循了 “让权等待”准则。此时S->value的绝对值表示在该信号量链表中已阻塞进程的数目。对信号量的每次signal操作表示执行进程释放一个单位资源,使系统中可供分配的该类资源数增加一个,故S -> value++操作表示资源数目加1。若加1后仍是S -> value<=0,则表示在该信号量链表中仍有等待该资源的进程被阻塞,故还应调用 wakeup原语,将S->list链表中的第一个等待进程唤醒。如果S -> value的初值为1,表示 只允许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。
  1. AND型信号量
    前面所述的进程互斥问题针对的是多个并发进程仅共享一个临界资源的情况。在有些应用场合,是一个进程往往需要获得两个或更多的共享资源后方能执行其任务。假定现有两个进程A和B,它们都要求访问共享数据D和E,当然,共享数据都应作为临界资源。 为此,可为这两个数据分别设置用于互斥的信号量Dmutex和Emutex,并令它们的初值都是1。相应地,在两个进程中都要包含两个对Dmutex和Emutex的操作,即
    在这里插入图片描述
    若进程A和B按下述次序交替执行wait操作:
    在这里插入图片描述
    最后,进程A和B就将处于僵持状态。在无外力作用下,两者都将无法从僵持状态中解脱 出来。我们称此时的进程A和B已进入死锁状态。显然,当进程同时要求的共享资源愈多 时,发生进程死锁的可能性也就愈大。
    AND同步机制的基本思想是:将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其它所 有可能为之分配的资源也不分配给它。亦即,对若干个临界资源的分配采取原子操作方式: 要么把它所请求的资源全部分配到进程,要么一个也不分配。 由死锁理论可知,这样就可避免上述死锁情况的发生。为此,在wait操作中增加了一个“AND”条件,故称为AND 同步,或称为同时wait操作,即Swait(Simultaneous wait)定义如下:
Swait(Sl, S2, Sn)
{
  while (TRUE)
  {
   if(Si>=l && ... && Sn>=l)
   {
    for (i =1; i<=n; i++)
      Si—; 
      break;
   }else
   {
   //place the process in the waiting queue associated with the first Si found withSi<l,
   // and set the program count of this process to the beginning of Swait operation 
   }
 }
}
Ssignal(Sl, S2, Sn) 
{
   while (TRUE)
   {
    (for (i=l; i<=n; i++)
    {
       Si++;
       //Remove all the process waiting in the queue
       // associated with Si into the ready queue
    }
   }
}
  1. 信号量集
    在前面所述的记录型信号量机制中,wait(S)或signal(S)操作仅能对信号量施以加1或减1操作,意味着每次只能对某类临界资源进行一个单位的申请或释放。当一次需要N个单位时,便要进行N次wait(S)操作,这显然是低效的,甚至会增加死锁的概率。此外,在 有些情况下,为确保系统的安全性,当所申请的资源数量低于某一下限值时,还必须进行管制,不予以分配。因此,当进程申请某类临界资源时,在每次分配之前,都必须测试资源的数量,判断是否大于可分配的下限值,决定是否予以分配。
    基于上述两点,可以对AND信号量机制加以扩充,对进程所申请的所有资源以及每类资源不同的资源需求量,在一次P、V原语操作中完成申请或释放。进程对信号量Si的测试值不再是1,而是该资源的分配下限值ti,即要求Si>=ti,否则不予分配。一旦允许分 配,进程对该资源的需求值为di,即表示资源占用量,进行Si= Si-di操作,而不是简单 的Si=Si-1。由此形成一般化的“信号量集”机制。对应的Swait和Ssignal格式为:
    在这里插入图片描述
    在这里插入图片描述

五、管制机制

虽然信号量机制是一种既方便、又有效的进程同步机制,但每个要访问临界资源的进 程都必须自备同步操作wait(S)和signal(S)。这就使大量的同步操作分散在各个进程中。这 不仅给系统的管理带来了麻烦,而且还会因同步操作的使用不当而导致系统死锁。这样, 在解决上述问题的过程中,便产生了一种新的进程同步工具——管程(Monitors)。

  • 管程的定义:系统中的各种硬件资源和软件资源,均可用数据结构加以抽象的描述,即用少量信息和对该资源所执行的操作来表征该资源,而忽略了它们的内部结构和实现细节。当共享资源用共享数据结构表示时,资源管理程序可用对该数据结构进行操作的一组过程来表示。如资源的请求与释放过程。我们把这样一组相关的数据结构和过程一并称为管程。
    hansan为管程所下的定义是:“一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据”。由定义可知,管程由四部分组成:
    ①管程的名称
    ②局部于管程的共享变量说明
    ③对该数据结构进行操作的一组过程
    ④对局部于管程的数据设置初始值的语句。
  • 注意: 局部于管程的数据结构,仅能被局部于管程的过程所访问,任何管程外的进程都不能访问;反之,局部于管程的过程也仅能访问管程内的数据结构。由此可见,管程相当于围墙,它把共享变量和对它进行操作的若干过程围了起来,所有进程要访问临界资源时,都必须经过管程(相当于通过围墙的门)才能进入,而管程每次只准许一个进程进入管程,从而实现了进程互斥。
  • 条件变量
    在利用管程实现进程同步时,必须设置两个同步操作原语wait和signal。当某进程通过管程请求临界资源而未能满足时,管程便调用wait原语使该进程等待,并将它排在等待队列上。仅当另一进程访问完并释放之后,管程又调用signal原语,唤醒等待队列中的队首进程。
    通常,等待的原因可有多个,为了区别它们,又引入了条件变量condition。管程中对每个条件变量,都赋予以说明,其形式为:
    var x,y:condition;。
    该变量应置于wait和signal之前,即可表示为x.wait和x.signal。例如,由于共享数据被占用而使调用进程等待,该条件变量的形式为nonbusy:condition。此时,wait原语应改为nonbusy.wait,相应地,signal应改为nonbusy.signal。
        应当指出,x.signal操作的作用,是重新启动一个被阻塞的进程,但如果没有进程被阻塞,则x.signal操作不产生任何后果,这与信号量机制中的signal操作不同。因为,后者总是要执行s:=s+1操作,因而总会改变信号量的状态。如果有进程Q处于阻塞状态,当进程P执行了x.signal操作后,怎样决定由哪个进程执行,哪个进程等待,可采用下述两种方式处理:
        ⑴P等待,直至Q离开管程,或等待另一条件
        ⑵Q等待,直至P离开管程,或等待另一条件。
        采用哪种处理方式,当然是各有其道理。但是Hoare却采用了第一种方式。而Hansan选择了两者的折中,他规定管程中的过程所执行的signal操作是过程体的最后一个操作,于是,进程P执行signal操作后立即退出管程,因而进程Q马上被恢复执行。
发布了10 篇原创文章 · 获赞 5 · 访问量 148

猜你喜欢

转载自blog.csdn.net/weixin_44715384/article/details/105010657