windows临界区和互斥量效率_我爱OS第9讲:进程的互斥与同步

本文介绍了操作系统中引入进程后带来的并发执行问题,特别是进程的异步性和对临界资源的访问。进程同步的四种准则被提出,包括空闲让进、忙则等待、有限等待和让权等待。信号量作为一种有效的同步机制,通过P、V操作实现对资源的互斥访问。文中还详细讨论了读者写者问题和哲学家进餐问题,并给出了相应的解决方案。最后提到了进程锁和条件变量的概念,作为信号量的抽象实现。
摘要由CSDN通过智能技术生成

7d1ecef1d2eefee98080474e50c46d4b.png

        在操作系统中引入进程后,虽然改善了资源利用率、提高了系统的吞吐量。但由于进程的异步性,也给系统造成了混乱,尤其是进程使用临界资源时,问题更为严重。例如,当多个进程争用一台打印机时,有可能多个进程的输出结果交织在一起,难于区分。 一、并发的原理         在单处理机多道程序环境系统中,进程被交替地执行,表现出一种并发执行的特征,即使不能实现真正的并行处理,进程间来回切换需要一定的开销,这种交替执行在处理效率上还是带来了很大的好处。但是由于并发执行的进程之间相对执行速度是不可预测的,它取决于其他进程的活动、操作系统的调度策略等。这就带来了以下的困难:         (1)全局变量的共享充满了危险。如果两个进程都使用了同一个全局变量,并且都对该变量进行了读写操作,那么不同的读写执行顺序是非常关键的。关于这个问题会在本节的下面举例说明。         (2)操作系统很难使资源的分配得到最佳的管理。如果某个进程请求使用某个特定的I/O 设备,并得到了这个设备,但该进程在使用该设备前被挂起了,操作系统仍然把这个设备锁定给该进程,不能分配给其他进程,因为操作系统不知道被挂起的进程何时又将执行。此外,资源分配还会导致死锁的危险。         (3)定位程序的错误是很困难的。这是因为并发程序的不确定性和不可再现性。         因此“并发”给操作系统的设计和管理带来了很多问题,操作系统为此要关注的事情有:         (1)操作系统必须记录每个进程的情况,并通过进程控制块实现。         (2)操作系统必须为每个进程分配和释放各种资源,这些资源包括:处理机、存储器、文件和 I/O 设备。         (3)操作系统必须保护每个进程的数据和资源,避免遭到其他进程的干涉和破坏。         (4)保证进程执行结果的正确性,进程的执行结果与速度无关。         以上四个问题中第( 2)、第( 3)个问题涉及到与存储管理、文件管理和设备管理相关的技术,本节重点要解决的是第(4)个问题。         我们以进程之间是否知道对方的存在,按进程的交互方式划分,可以分为以下三种情况:         (1)进程之间不知道对方的存在。这是一些独立的进程,它们不会一起工作。只是无意地同时存在着。尽管这些进程不一起工作,但是操作系统需要知道它们对资源的竞争情况。例如,两个无关的进程都要使用同一磁盘文件或打印机,操作系统必须控制和管理对它们的访问。         (2)进程间接知道对方。进程并不需要知道对方的进程标识符,但它们共享某些数据,它们在共享数据时要进行合作。         (3)进程直接得知对方。进程通过进程标识符互相通信,用于合作完成某些任务。进程的并发执行,使进程之间存在着交互,进程间的交互关系包括互斥、同步和通信。进程互斥是指由于共享资源所要求的排它性,进程之间要相互竞争,某个进程使用这种资源时,其他进程必须等待。换句话说,互斥是指多个进程不能同时使用同一个资源。这种情况下,进程之间知道对方的程度最低。         进程同步是指多个进程中发生的事件存在着某种时序关系,必须协同动作、相互配合,以共同完成一个任务。进程同步的主要任务,是使并发执行的诸进程有效地共享资源和相互合作,从而使程序的执行具有可再现性。这种情况,比进程之间的互斥知道对方的程度要高,因为进程之间要合作。         进程通信是指多进程之间要传递一定的信息。这种情况下,进程之间知道对方的程度最高,需要传递的信息量也最大。 二、临界资源与临界区 1.临界区与临界资源         在计算机中许多资源只能允许一个进程使用,如果多个进程同时使用这类资源就会引起激烈的竞争。操作系统必须保护这些资源,以防止两个或两个以上的进程同时访问它们。我们把那些在某段时间内只允许一个进程使用的资源称为临界资源(Critical Resource) 。         几个进程共享同一临界资源,它们必须以互相排斥的方式使用临界资源,即当一个进程正在使用临界资源且尚未使用完毕时,其他进程必须延迟对该资源的进一步操作,在当前的进程使用完毕之前,不能从中插入使用这个临界资源,否则将会造成信息混乱和操作出错。系统中同时存在许多进程,它们共享各种资源,然而有些资源每次只能让一个进程使用。         两个进程在执行时必须等一个进程执行完毕,另一个进程才可以执行。         不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它们进行访问。我们把在每个进程中访问临界资源的那段代码称为临界区(Critical Section)。显然,若能保证诸进程互斥地进入临界区,就可实现它们对临界资源的互斥访问。为此,每个进程在进入临界区之前应对要访问的临界资源进行检查,看它是否正在被访问。如果此刻临界资源未被访问,进程便可以进入临界区,对资源进行访问,并设置它正被访问的标志;如果此刻临界资源正被某进程访问,则进程不能进入临界区。因此,必须在临界区前面增加一段用于进行上述检查的代码,这段代码称为进入区(Enter Section)。相应地,在临界区后面也要加上一段称为退出区(Exit Section)的代码,用于将临界区正被访问的标志恢复为未被访问标志。进程中除去上述进入区、临界区及退出区之外的其他部分的代码,称为剩余区(Remainder Section)。这样,可把一个访问临界资源的进程描述为: 5b235ca22437ce49fd8be479db7934f0.png 2.同步机制必须遵循的四个准则         为了实现进程互斥,可用软件的方法在系统中设置专门的同步机制来协调多个进程,但所有的同步机制都必须遵循下述四个准则:         (1)空闲让进。当无进程处于临界区时,临界资源处于空闲状态。此时允许进程进入临界区。         (2)忙则等待。当已有进程进入临界区时,临界资源正在被访问,其他想进入临界区的进程必须等待。         (3)有限等待。对于要求访问临界资源的进程,应保证在有效的时间内进入,以免进入“死等”状态。         (4)让权等待。当进程不能进入临界区时,应立即释放处理机,以免其他进程进入“忙等”。         有许多方法可以实现互斥,一种方法是让希望并发执行的进程自己来完成。不论是系统 程序还是应用程序,当需要与另一个进程互斥时,不需要操作系统提供任何支持,自己通过 软件来完成。 尽管该方法已经被证明会增加许多处理开销和错误,但通过对这种方法的分析,可以更好地理解并发处理的复杂性,第二种方法是使用专门的机器指令来完成,这种方法的优点是可以减少开销,但与具体的硬件系统相关,很难成为一种通用的解决方案。第三种方法是由操作系统中提供某种支持。 三、信号量         1965 年,荷兰学者 Dijkstra 提出的信号量机制是一种卓有成效的解决进程同步问题的工具。该机制提出后,在很长时期内得到了广泛的应用,并取得了很大的进展,它从整型信号量发展到记录型信号量,继而又发展到一般“信号量集”。 1.信号量和 PV 操作         Dijkstra 最初定义的信号量包括一个整型值 S 和一个等待队列 s.queue,信号量只能通过两个原语 P V 操作来访问它,信号量的定义为: struct semaphore{     int value;     struct QUEUETYPE *queue; }         P 原语所执行的操作可用下面的函数 wait(s)来表示: void wait(semaphore s) {  s.value = s.value - 1;  if (s.value < 0)     block(s.queue); /* 将进程阻塞,并将其投入等待队列 s.queue */ }         V 原语所执行的操作可用下面的函数 signal(s)来表示: void signal(semaphore s) {  s.value = s.value + 1;  if (s.value <= 0)     wackup(s.queue);/* 唤醒被阻塞进程,将其从等待队列 s.queue 取出,投入就绪队列*/ }         在信号量机制中,信号量的初值 s.value 表示系统中某种资源的数目,因而又称为资源信号量。         P 操作意味着进程请求一个资源,因此描述为 s.value=s.value−1;当 s.value<0 时,表示资源已经分配完毕,因而不能够满足进程所申请的资源,进程无法继续执行,所以进程执行block(s.queue)自我阻塞,放弃处理机,并插入到等待该信号量的等待队列。         V 操作意味着进程释放一个资源,因此描述为 s.value=s.value+1;当 s.value≤0 时,表示在该信号量的等待队列中有等待该资源的进程被阻塞,故应调用 wakeup(s.queue)原语将等待队列中的一个进程换醒。         如果信号量的初值为 1,表示仅允许一个进程访问临界区,此时的信号量转换为互斥信号量。P 操作和 V 操作分别置于进入区和退出区,如定义 mutex 为互斥信号量,其初值为 1,代码段如下: f3c21980a691b33b2a7f8b890689d3a4.png         利用信号量可以实现进程之间的同步,即可以控制进程执行的先后次序。如果有两个进程 P1 和 P2,要求 P 2 必须在 P1 执行完毕之后才可以执行,则只需要设置一个信号量 S,其初值为 0,将 V(S)操作放在进程 P1 的代码段 C1 后面,将 P(S )操作放在进程 P2 的代码段C2 前面,即: 12e872fc29a9535d0b690d9327b6d05d.png         对于图 2-22 所示的进程关系:有四个并发执行的进程 P1、P2、P3 和 P4,它们之间的关系是 P1 首先被执行;P1 执行完毕 P2、P3 才能执行;而 P4 只有在 P2 执行完毕后才能执行。为了实现它们之间的同步关系,可以写出如下的并发程序。 9dc76ac647152eac76557380e1d47eb2.png 2. AND 信号量         当利用信号量解决了单个资源的互斥访问后,下面我们讨论控制进程对多个资源的互斥访问问题。在有些应用中,一个进程需要先获得两个或更多的共享资源后,方能执行其任务。假如有两个进程 P1 和 P2,它们要共享两个共享资源 R1 和 R2,为此要设置两个互斥信号量mutex1 和 mutex2,并令它们的初值为 1,相应地,两个进程都要包含对信号量 mutex1和mutex2: /* 进程 P1 */           /* 进程 P2 */ P(mutex1);             P(mutex2); P(mutex2);             P(mutex1); ......                         ......         如果进程 P1 和 P2 交替地执行 P 操作如下:         进程 P1 执行 P(mutex1);于是 mutex1=0;         进程 P2 执行 P(mutex2);于是 mutex2=0;         进程 P1 执行 P(mutex2);于是 mutex2=-1;进程 P1 阻塞;         进程 P2 执行 P(mutex1);于是 mutex1=-1;进程 P2 阻塞;         此时,两个进程处于僵持状态,都无法继续运行。         AND 信号量同步机制就是要解决这样的问题,其基本思想是,将进程在整个运行期间所需要的所有临界资源,一次性地全部分配给进程,待该进程使用完后再一起释放。只要尚有一个资源不能满足进程的要求,其他所有能分配给该进程的资源都不予以分配。为此,在 P操作上增加一个“AND”条件,故称为 AND 信号量。P 操作的原语为 Swait, V 操作的原语为 Ssignal。在 Swait 中,各个信号量的次序并不重要,尽管会影响进程进入哪个等待队列。由于 Swait 实施对资源的全部分配,进程获得全部资源并执行之后再释放全部资源,因此避免了前面所述的僵持状态。下面是 Swait 和 Ssignal 的伪代码。 e04ddb1f4094974c40fcf86112bbcaef.png 四、经典进程同步问题 1.读者、写者问题         一个数据对象若被多个并发进程所共享,且其中一些进程只要求读该数据对象的内容,而另一些进程则要求写操作,对此,我们把只想读的进程称为“读者”,而把要求写的进程称为“写者”。在读者、写者问题中,任何时刻要求“写者”最多只允许有一个执行,而“读者”则允许有多个同时执行。因为多个“读者”的行为互不干扰,他们只是读数据,而不会改变数据对象的内容,而“写者”则不同,他们要改变数据对象的内容,如果他们同时操作,则数据对象的内容将会变得不可知。所以对共享资源的读写操作的限制条件是: 允许任意多的读进程同时读;         一次只允许一个写进程进行写操作;         如果有一个写进程正在进行写操作,禁止任何读进程进行读操作。         (1)用信号量解决“读者、写者”问题         为了解决该问题,我们只需解决“写者与写者”和“写者与第一个读者”的互斥问题即可,为此我们引入一个互斥信号量 Wmutex,为了记录谁是第一个读者,我们用一个共享整型变量 Rcount 作一个计数器。而在解决问题的过程中,由于我们使用了共享变量 Rcount,该变量又是一个临界资源,对于它的访问仍需要互斥进行,所以需要一个互斥信号量 Rmutex,算法如下: 5953e7bd218c89063397279e64a68e63.png 2.哲学家进餐问题         哲学家进餐问题是一个典型的同步问题,它由 Dijstra 提出并解决。该问题描述有五个哲学家,他们的生活方式是交替地思考和进餐。哲学家们共用一张圆桌,围绕着圆桌而坐,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时拿起其左、右的两支筷子,试图进餐,进餐完毕又进行思考。这里的问题是哲学家只有拿到靠近他的两支筷子才能进餐,而拿到两支筷子的条件是他的左、右邻居此时都没有进餐。         分析可知,筷子是临界资源,一次只允许一个哲学家使用。因此,我们可以用互斥信号量来实现。其描述如下: c0d4f6e442050f20e26d0c7074496008.png         在以上的描述中,虽然解决了两个相邻的哲学家不会同时进餐的问题,但是有一个严重的问题,如果所有的哲学家总是先拿左边的筷子,再拿右边的筷子,那么就有可能出现这样的情况,五个哲学家都拿起了左边的筷子,而当他们想拿右边的筷子时,却因为筷子已被别的哲学家拿去,而无法拿到。此时所有的哲学家都不能进餐。这就出现了死锁现象。 信号量在解决进程互斥和同步问题时是一个非常有效的工具,但是应该看到,如果使用不当,可能引起死锁。请读者思考,写出一个用信号量解决哲学家进餐问题的不产生死锁的算法。         下面我们讨论用 AND 信号量解决哲学家进餐问题。在该问题中,筷子是临界资源,而题目中的临界资源有五个,每个哲学家需要拿到两个临界资源才可以进餐,所以为了避免死锁的产生,哲学家在申请临界资源时必须一次性地申请其所需要的所有资源。具体解法如下: 2d8be29aa6f86d30cee9292b13f061e5.png 五、关于“锁”和“条件变量”         进程锁是操作系统为方便用户使用信号量而提出的一种机制,最典型的互斥锁就是初始值为1的信号量。而与互斥锁配合使用的条件变量则是一组信号量,初始值分别为1和0,信号量A初始值为1,用户对互斥资源的保护,初始值为0的信号量用户两个进程之间同步。         下面给出用信号量实现互斥锁和条件变量的实现方式:         互斥锁与条件变量: a5aa9724eb5f7f5c3b823170b52be5df.png         用信号量实现: 097631883af102d56462a5f4148f1674.png 本文摘自: 清华大学出版社《计算机操作系统》郁红英、李春强编著

7c2be32335e12f6f76a0aa5f46087f03.png    6c38c6fb402e30651f35890f85586077.png  

86c64a314f251c37d6fd3f9218560dc3.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值