操作系统中的同步

2025年7月11日 | 阅读 8 分钟

同步是操作系统的一个基本概念,简单来说,它处理的是当访问公共资源时进程或线程的执行协调。它旨在防止竞态条件,并确保在多任务处理环境中并发执行的操作以线程安全的方式处理,从而防止数据损坏。

为什么需要同步

当前大多数操作系统都支持任务和线程,允许同时执行多个进程或线程。然而,当这些进程访问公共资源(如内存、文件或输入/输出设备)时,可能会发生冲突,导致

  1. 竞态条件: 当两个或多个进程尝试同时更改共享数据时,结果将取决于执行顺序,而该顺序是不可控的,那么这种情况就称为竞态条件。
  2. 死锁: 基本上,当两个进程在同一时间需要相同资源时,这种情况就称为死锁。
  3. 数据不一致: 没有同步的并发访问可能导致数据损坏或不一致。

进程同步的类型

操作系统有两种主要的进程同步类型:

  • 竞争性: 当两个或两个以上进程争夺对同一资源的访问权时,它们就被称为处于竞争性同步状态。
  • 竞争性进程不同步可能导致不一致或数据丢失。
  • 协作性: 当两个或两个以上的进程相互影响,也就是说,一个进程的执行对另一个进程有影响时,它们就被称为处于协作性同步状态。
  • 协作进程之间缺乏同步可能导致死锁。

同步机制

操作系统提供了一系列同步进程的方法,使它们的交互安全且可预测。

1. 锁

锁是最简单的同步原语之一。它们限制对共享资源的访问,因此多个线程无法同时访问它。

  • 互斥锁: 一次只允许一个线程访问资源的锁称为互斥锁。
  • 自旋锁: 一种锁,它使线程在一个循环中等待(忙等待),直到锁被释放。

2. 信号量

信号量是控制对共享资源的访问的信号机制,通过维护可用资源的计数来实现。

  • 二元信号量: 功能类似于互斥锁,允许一个线程访问资源。
  • 计数信号量: 它允许多个线程对资源进行有限访问。

3. 监视器

监视器是一种高级同步原语,它封装了互斥和等待(信号)以满足特定条件。

4. 栅栏

栅栏同步一组线程,它会等待直到组中的所有线程都到达某个执行点,然后才恢复。

5. 消息传递

测试生成机制由通信和同步进程发送和接收消息组成,以防止直接访问共享资源。

同步的前提条件

关键区问题的解决方案必须满足以下三个条件:

互斥: 当一个进程在关键区运行时,不允许其他进程在其中操作。

前进: 如果没有进程在关键区内运行,并且有其他进程在等待运行,那么任何线程都应该被允许访问关键区。只有那些不在关键区运行的进程才决定哪个进程将进入关键区。

无饥饿: 一个进程在等待了很长时间后仍然没有机会进入关键区,就被称为饥饿。无饥饿也可以称为有界等待。

  • 进程进入关键区不应该花费永恒的时间。
  • 当一个进程请求访问关键区时,允许在它之前进入关键区的进程数量应该是有限的。
  • 一旦建立了这个限制,该进程就应该能够访问关键区。

关键区问题:是什么?

关键区是一段代码,它总是只能由一个进程访问。这意味着当多个应用程序希望访问和修改单个共享数据时,一次只能允许一个进程对其进行更改。在数据可用之前,其余操作必须等待。

当 wait() 函数主要处理进入关键区时,signal() 函数则负责离开关键区。如果我们排除了关键区,我们就无法确保所有进程并发运行后的最终结果是一致的。

我们将探讨关键区问题的一些解决方案。

关键区问题解决方案

Peterson 解决方案

Peterson 解决关键区问题的方案被广泛使用。该方案基于传统的软件。此技术可确保一次只有一个进程在关键区运行。它基于这样一个概念:当一个进程在关键区运行时,另一个进程运行剩余的代码,反之亦然。Peterson 方案中的进程使用两个共享变量。

  • 布尔数组 Flag[]: 一个布尔数组,其初始值为 FALSE。希望进入关键区的进程由该标志数组指示。
  • int Turn: 一个整数变量。Turn 指示准备进入关键区的进程编号。

让我们看一下 Peterson 方案的一些缺点

  • Peterson 方案是忙等待。
  • 此外,该方案仅限于两个进程。

同步硬件 

硬件有时可以帮助解决关键区问题。某些操作系统提供了锁功能。当一个进程进入关键区时,会对其施加一个锁,并且该进程必须释放该锁才能离开关键区。因此,如果一个关键区已经被一个进程使用,那么其他进程就无法访问它。锁的值可以是 0 或 1。

互斥锁

互斥锁的创建是因为同步硬件的实现是一个复杂的过程。一种称为互斥的锁定技术用于同步关键区中的资源访问。通过这种方式,我们在关键区上使用一个 LOCK。当一个进程从入口段进入时,LOCK 被设置;当它从出口段离开时,LOCK 被取消设置。

信号量

与互斥锁不同,信号量是信号机制,进程可以使用它们来与正在等待信号量的另一个进程进行通信。互斥锁只能由创建共享锁的进程来通知。信号量使用 wait() 和 signal() 过程来保持进程同步。

同步问题

为了理解和实现有效的同步机制,我们研究一些经典的同步问题:

  1. 生产者-消费者问题 生产者和消费者之间共享一个缓冲区,并且要求双方都不能覆盖或读取空槽。
  2. 读者-写者问题 这里的想法是平衡读写操作,使它们不冲突,并优先写者执行写操作。
  3. 就餐哲学家问题 这个问题主要演示了在不导致死锁或饥饿的情况下,为多个不同进程分配有限资源的困难。

高级同步技术

随着系统复杂性的不断增加,引入了更高级的同步技术和算法。

  1. 读-复制-更新 (RCU): 一种适用于读密集型工作负载的同步方法。它允许读取者在以非阻塞方式应用更新时读取数据。
  2. 事务内存: 它允许代码块以某种事务方式运行,仅在没有冲突发生时才提交更改。
  3. 优先级反转处理: 一种涉及处理称为优先级反转的结构性障碍的机制,其中高优先级任务的资源被低优先级任务持有。解决此问题的一种方法是使用优先级继承。
  4. 线程池: 一组最优创建的线程,可用于运行任务。线程池会自行处理同步。

硬件对同步的支持

现代处理器具有有助于同步的硬件指令:

  1. Test-and-Set: 一条原子指令,它在单个操作中检查和更新内存位置,通常用于实现锁。
  2. Compare-and-Swap (CAS): 仅当内存位置与指定值匹配时,才原子地更新它,该指令用于许多无锁算法。
  3. 内存屏障: 它确保读写操作的正确排序,这在多处理器系统中至关重要。

同步中的挑战

  1. 死锁: 必须通过正确的设计来避免循环依赖,以防止死锁。
  2. 饥饿: 由于高优先级进程总是先访问资源,某些进程可能会被强制延迟。
  3. 性能开销: 同步机制会增加延迟并降低系统吞吐量。
  4. 可扩展性问题: 随着线程数量的增加,同步成本也可能增加,这可能会抵消并行性的好处。

同步的实际应用

  1. 数据库管理系统 当存在并发事务时,同步可确保数据一致性。
  2. Web 服务器: 通过同步对缓存和文件等共享资源的访问来管理多个客户端请求。
  3. 操作系统: 通过同步处理进程调度、资源分配和进程间通信。
  4. 嵌入式系统: 在汽车控制和医疗设备等系统中至关重要,这些系统中精确的时序和协调至关重要。

结论

同步是所有操作系统的重要组成部分,它允许多个进程并行执行而不相互干扰。同步允许以有序且无冲突的方式使用共享资源,例如使用锁、信号量和消息传递等机制。为了确保系统在多任务场景下保持其稳定性和性能,必须进行适当的同步。然而,随着更高级技术和特定硬件支持的出现,同步已成为当今许多计算系统中一个不断演进的问题。

常见问题

为什么需要进程同步?

当进程必须同时运行时,同步就变得必要。实现无障碍资源共享是同步的主要目标。

操作系统的竞态条件是什么?

当两个或多个线程在访问共享数据时尝试同时修改它时,就会发生竞态条件。由于线程调度机制可以随时在线程之间切换,因此您不知道哪个线程会先尝试访问共享数据。

什么是操作系统的生产者-消费者问题?

在生产者-消费者问题中,必须填充和耗尽一个有界缓冲区。当缓冲区已满时,生产者可以在插入项目后进入睡眠状态。如果缓冲区为空,则消费者必须进入睡眠状态以从缓冲区获取数据。