我们已经看到进程可以并发的或者并行的执行。 3.2.2节介绍了进程调度的作用,并描述了CPU调度程序如何在进程之间快速切换以提供并发执行。这意味着一个进程可能只在另一个进程被调度之前完成部分的执行。事实上,一个进程可能会在其指令流中的任意点被中断,而处理器核心可能被指派来执行另一个进程的指令。另外,第4.2节介绍了并行执行,其中两个指令流(代表不同的进程)在单独的处理核心上同时执行。在本章中,我们解释了并发或并行执行如何导致涉及多个进程共享的数据完整性的问题。

让我们考虑一个例子来说明这是如何发生的。在第3章中,我们开发了一个系统模型,该系统由协作的顺序进程或线程组成,所有的运行都是异步的,并且之间可能共享数据。我们用生产者-消费者问题来说明这个模型,它是操作系统的代表。特别地,在第3.4.1节中,我们描述了如何使用限界缓冲区来允许进程来共享内存。

现在我们回到对限界缓冲区的考虑。正如我们指出的那样,我们最初的解决方案允许最多BUFFER_SIZE − 1个条目同时在缓冲中。假设我们想修改算法来弥补这个缺陷。一种可能是添加一个整型变量counter,将其初始化为0。每当我们在缓冲区中添加新条目时,counter都会递增,每次从缓冲区中移除一个条目时,count都会递减。生产者进程的代码可以修改如下:

while (true) {
    /* produce an item in next produced */
    while (counter == BUFFER_SIZE )
        ; /* do nothing */
    buffer[in] = next produced;
    in = (in + 1) % BUFFER_SIZE ;
    counter++;
}

消费者进程的代码可以修改如下:

while (true) {
while (counter == 0)
    ; /* do nothing */
    next consumed = buffer[out];
    out = (out + 1) % BUFFER SIZE ;
    counter--;
    /* consume the item in next consumed */
}

虽然上面所示的生产者和消费者例程是正确的,但在同时执行时,它们可能不能正确地运行。举例来说,假设变量counter的值为5,并且生产者和使用者流程同时执行“counter++”和“counter—”的语句。在执行这两个语句之后,变量计数器的值可能是4、5或6!不过,唯一正确的结果是counter == 5,如果生产者和使用者分别执行,则生成的结果是正确的。

我们可以证明counter的值可能不正确。请注意,“counter++”语句可以像下面这样用机器语言来实现(在典型机器上):

register1 = counter
register1 = register1 + 1
counter = register1

register1 是一个本地CPU寄存器。类似地,语句“counter--”实现如下:

register2 = counter
register2 = register2 − 1
counter = register2

register2是一个本地CPU寄存器。尽管register1 和register2可能是相同的物理寄存器(比如一个累加器),但请记住,这个寄存器的内容将被中断处理器(interrupt handler)保存和恢复(第1.2.3节)。

“counter++”和“counter--”的并发执行等同于一个顺序执行,在这个过程中,前面提到的低级语句会以任意顺序交错(但每个高级语句中的顺序被保留)。其中一个这样的交错如下:

T0 : producer execute register1 = counter {register1 = 5}
T1 : producer execute register1 = register1 + 1 {register 1 = 6}
T2 : consumer execute register2 = counter {register2 = 5}
T3 : consumer execute register2 = register2 − 1 {register2 = 4}
T4 : producer execute counter = register1 {counter = 6}
T5 : consumer execute counter = register2 {counter = 4}

请注意,我们已经到达了不正确的状态“counter == 4”,这表明有四个缓冲区已满,而实际上,是有5个缓冲区已满。如果我们颠倒了T4和T5的语句的顺序,我们就会到达不正确的状态“counter == 6”。

因为我们允许两个进程同时操作变量计数器,所以我们将到达这个不正确的状态。这样的情况下,多个进程并发的访问和操作相同的数据,执行的结果取决于访问发生的特定顺序,称为竞态条件( race condition)。为了防止上面的竞态条件,我们需要确保每次只有一个进程可以操纵变量counter。为了做出这样的保证,我们要求进程在某种程度上是同步的。

当系统中不同的部分操作资源时,操作系统中经常会出现这样的情况。此外,正如我们在前几章中所强调的,多核系统的重要性日益增加,因此越来越强调开发多线程应用程序。在这样的应用程序中,多个线程-------很可能之间共享数据----------在不同的处理核心上并行运行。

显然,我们希望这些活动产生的任何变化不会相互干扰。由于这个问题的重要性,我们把大部分时间都花在了这个问题上。本章在进程同步和协作进程协调行。

results matching ""

    No results matching ""