【Linux】Linux的同步控制

版权声明:本文为博主原创文章,允许转载,但希望标注转载来源。 https://blog.csdn.net/qq_38410730/article/details/81637877

在多任务系统中,通常会有多个进程以并发的方法在运行,这种并发活动与单任务系统相比有着截然不同的一些特征:

  • 由于进程何时运行、何时中止不由程序设计者来掌握,而是由调度器来决定,即并发活动的随机性;
  • 由于系统资源数量的限制,必然会使多个需要同时使用同一资源的进程发生冲突,即并发活动的竞争性;
  • 由于共享资源的存在,使得进程在共享资源上的活动能够被其它进程了解和干涉,即并发活动的开放性;
  • 当多个进程需要共同配合来完成一项工作时,必然要产生相互的制约,即并发活动的制约性;
  • 为协同工作,各个进程之间要有必然的交互,即并发活动具有交互性。

其实,仔细分析一下就会知道,引起进程之间产生各种复杂关系的原因主要就是两个:一是共享资源的存在;二是因完成同一项任务而需要协作。共享资源的存在导致了进程的竞争,协作的需要导致了同步。所以,操作系统必须提供相应的手段对竞争进行必要的控制,同时还要为进程提供必要的同步手段。

现代操作系统用于进程竞争控制的有效手段是信号量以及信号量集。

竞争、临界区与互斥

在多任务系统中,当进程因使用共享资源而发生竞争时,操作系统必须对竞争进行必要的控制,以使进程可以有序地使用共享资源而不发生错误。

进程竞争

本来,进程不记录其它进程的任何情况,并且有自己的私有运行空间,它们之间各自独立、彼此无关。但遗憾的是,它们总是可能需要使用打印机等共享资源,那么当多个进程在同一个时间要使用同一个共享资源时,就一定会出现争抢现象,这种现象就叫做进程的竞争。

例如:进程A、B都需要使用打印机,进程A先获取使用权,但在其未打印完成就因其时间片已到,调度器将打印机的使用权分配给了进程B。那么就会将B需要打印的内容打印到A未完成的后面,显然,这是错误的!

可以这么认为,共享资源是“危险物”,而与共享资源有关的这些程序段就是程序中事故高发段——“危险段”。当两个以上的“危险段”并发时,就可能会发生严重事故。

临界段

在计算机技术中,将程序中与共享资源有关的危险程序段称为“临界段”,而共享资源叫做“临界资源”,这是两个由Dijkstra在1965年提出并沿用至今的概念。

仔细分析一下就会知道,增加资源以避免竞争时解决上述问题最简单的方法。但在计算机系统往往由于成本原因不可能这么办,那么剩下的就只剩下一个方法:杜绝不同进程临界段的同时执行。

具体方法为:使程序中的临界段为原子操作,即临界段的执行不被任何其他代码所中断;也就是说,令多个进程中与同一个共享资源相关的临界段在执行上是互相排斥的,简称“互斥”。

在这里附带说一下,操作系统中还有很多处于某种原因(不一定与共享资源相关)必须连续运行而不允许中断的操作代码段,这种代码段叫做“原语”。

信号量与P、V操作

在临界段占用共享资源期间,使该资源暂时成为该临界段的独占资源是实现互斥的核心思想。

信号量的基本概念

在前面学习UCOSIII时已经知道,用只有两个值的信号量可以很好地解决互斥问题,人们也常常把这种信号量叫做互斥型信号量。下图是两个进程使用互斥型信号量无冲突的访问一个共享资源的示意图:

进程1在访问共享资源之前先进行请求信号量的操作,当进程1发现信号量的标志位1时,它一方面把信号量的标志由1改为0,另一方面进行共享资源的访问。如果进程2在进程1已经获得信号量之后再请求信号量,那么因为它获得的标志值为0,所以进程1就只能等待而不能访问共享资源了。显然,这种做法可以有效地防止两个进程同时访问同一个共享资源所造成的冲突。

那么进程2何时可以访问共享资源呢?当然是在进程1使用完共享资源之后,由进程1向信号量发信号使信号量标志的值由0再变成1时,进程2就有机会访问共享资源了。同进程1一样,进程2一旦获得了共享资源的访问权,那么在访问共享资源之前一定要把信号量标志的值由1变成0。

信号量的P和V操作

从前面叙述可知,表示共享资源被占用情况标志可以是一个整型变量s,除了初始化外,该变量仅能通过两个由进程使用的操作原语P和V来改变它。其中P叫做测试原语(Problem),V叫做增量原语(Verhogen)。

如果把对s的P操作记为P(s),对s的V操作记为V(s),那么,当进程使用P(s)时,P(s)将产生如下动作:

  • 若s大于0时,把信号量s减1,该进程继续运行;
  • 否则进程进入等待状态,直至其他进程对s进行V(s)操作,使s大于0为止。
  • 当进程使用V(s)时,V(s)将产生如下动作:把变量s加1,即s=s+1。

信号量的基本组成

综上所述,组成信号量的基本要素为:一个整型变量(标志)、一组操作(其中必须包含P和V两个操作)和一个队列(等待进程队列)。

其中,等待队列的作用就是使暂时未获得允许而处于等待状态的进程将自己的PCB加入等待队列,而进程本身则转入阻塞状态。

临界段代码格式

如果把信号量用于互斥,那么信号量的初值通常为1,因为很少有共享资源在系统一开机就被占用的。这样在临界段的前面使用P操作,而在临界段末尾使用V操作。

例如:设置一个整数变量m作为资源占用情况标志,并分别定义操作P(m)和V(m),那么进程的临界段代码就可以按照下面的形式来进行编写:

{
        P(m)
                临界段
        V(m)
}

其中,m的初值为1。

竞争来源于进程之间的间接制约,形成这个间接制约的中介物就是共享资源,信号量则使共享该那个资源变成暂时的独占资源,从而解决了进程因竞争而产生的各种问题。

用信号量实现同步

如果有缓冲区如下图,进程A负责向缓冲区写数据,进程B负责从缓冲区读取进程A写入的数据,那么进程A就是生产者,进程B就是消费者,而缓冲区就是两个进程都要使用的共享资源。

显然,这两个进程与缓冲区相关代码的执行顺序应该是进程A在前,进程B在后,否则就会出现错误。

为保证正确地执行时序,可以设置两个信号量s1和s2。其中,s1表示缓冲区是否为空(0表示非空,1表示空),初值为1;s2表示缓冲区是否为满(0表示非满,1表示满),初值为0。在进程A和进程B中与缓冲区相关的代码为:

进程A(生产者)的代码:

while(1) {
        P(s1)
                对共享资源写操作
        V(s2)
}

进程B(消费者)的代码:

while(1) {
        P(s2)
                对共享资源读操作
        V(s1)
}

猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/81637877