竞态条件在操作系统中的意义
在本教程中,我们将学习操作系统中的竞态条件。
今天,我们将学习操作系统中最重要的概念。竞态条件通常发生在操作系统中的多线程概念中。
竞态条件通常发生在有多个开关的灯管中。这个带有多个开关的灯管是竞态条件在操作系统中发生的最大例子。
竞态条件也会在进程的情况下发生。如果我们不好好处理这种竞态条件,那么我们可能会陷入死锁。
现在,让我们首先了解多线程的基础知识。
操作系统中的多线程
多线程是一种将任务分成多个单线程的过程。这些线程按顺序或非顺序方式一个接一个地执行以完成任务。
同样的过程或作业可以由多个线程进行多线程处理,其中线程在其他任务中执行相同的处理,我们需要这种类型的线程来完成任务,或者我们可以说该任务由多个线程执行。使用多线程可以进行多任务处理。
在多线程概念中有三种类型的线程。它们是:
- 多对多的多线程风格
- 多对一的多线程风格
- 一对一的多线程风格
操作系统中的竞态条件
多线程场景中的竞态条件
当许多线程共享资源或在多线程上下文中执行相同的代码时,就会出现竞态条件。不适当地处理可能导致不利情况,其中输出状态取决于线程执行顺序。
多进程场景中的竞态条件
竞态条件是一种情况,当设备或系统尝试同时执行两个或更多操作时,由于设备或系统的性质,这些操作必须按正确顺序执行才能成功执行;竞态条件是一种不良情况。
竞争与编程和计算机科学最常见的关联是这些。当两个计算机程序进程尝试同时访问同一资源时,它们会发生并破坏系统。
临界区域问题中的竞态条件
竞态条件是在关键部分区域内可能发生的情况。当在关键区域中执行多个线程的不同结果取决于线程的执行顺序时,这种情况就会发生。
如果将关键区域视为原子指令,则可以避免某些区域中的竞争条件。通过使用锁或原子变量来正确同步线程,也可以避免竞争问题。
竞态条件是一种跨学科的方法。它可以在多线程、进程执行和临界区域的概念中发生。
竞态条件的例子:
例子1:灯管开关
竞态条件通常发生在有多个开关的灯管中。这个带有多个开关的灯管是竞态条件在操作系统中发生的最大例子。
解释
考虑一个灯管,两个开关。让两个开关都连接到灯管上,而灯管处于关闭状态。在这里,如果打开开关1,灯管就会打开。然后,如果当开关1处于打开状态时我们打开开关2,灯管将关闭。但是,两个开关都处于打开状态,而灯管处于关闭状态。
现在,让我们假设灯管处于关闭状态,开关1和开关2都处于关闭状态。如果我们同时打开两个开关,那么灯管只会处于打开状态。这是由于电路断路器。电路中的一个开关被断路器绊倒了。这是为了防止开关的功能变得无关紧要。
如果同样的情况在计算机中发生会怎么样?在这里,ON 和 OFF 条件被读取和写入操作所替换。在这里,灯管被替换为计算机。想象一下,如果我们在旧数据正在被读取时重新写入计算机上的数据会发生什么。由于这种状态,可能会发生以下情况:
- 新写入数据的错误和故障
- 旧写入数据的错误和故障
- 计算机可能会崩溃
- 数据损坏可能会发生。
- 存储的数据可能无序。
竞态条件会导致输出不一致,降低我们应用程序的耐久性和信心。
在操作系统中,通过线程实现并发。同时执行多个操作的能力称为并发性。操作系统通过线程实现并发性。如果多个线程在未与彼此同步访问共享数据,则可能遇到处理共享数据的线程每次提供不同结果的情况。
在操作系统中识别或检测竞态条件
找到竞争条件的必要性非常重要。如果我们未能识别它们,那么我们将会失去很多数据,并且已经存在的数据也会变得损坏。因此,对于用户和计算机来说,在操作系统中找到竞态条件的发生非常重要。
发现和检测竞态条件被认为是具有挑战性的。它们是可能由多种潜在编码错误导致的语义问题。最好从一开始就编写避免这些问题的代码。
程序员使用静态和动态分析工具来查找竞争条件。不启动软件,静态测试工具会扫描所有内容。然而,它们往往会产生大量不准确的报告。虽然动态分析方法提供较少的误报,但它们可能会错过程序本身没有出现的竞争条件。
数据竞争有时会导致竞态条件,当两个线程同时针对同一内存区域,其中至少一个执行写操作时。相对于竞争条件,数据竞争更容易被发现,因为它们需要特定的情况才能显现。像 Go 项目的 Data Race Detector 这样的工具会监视数据竞争场景。竞争状态提供了更重要的问题,并且与应用程序语义更紧密相关。
生产者和消费者问题中的竞态条件
竞态条件通常发生在生产者和消费者问题的情况下。这个生产者和消费者问题是竞态条件在操作系统中发生的最大例子。
让我们了解一下什么是生产者和消费者问题。
生产者和消费者问题
生产者和消费者问题是有界缓冲问题的另一个名称。在这个问题中,缓冲区中有 n 个插槽,每个插槽可以容纳一个数据单元。生产者和消费者是使用缓冲区的两个操作。生产者和消费者问题属于同步的经典问题。
在这里,生产者试图创建新的数据或更改现有的数据。
消费者仅尝试读取数据。它不会尝试更改已有的数据。
在这个生产者和消费者问题中有两种情况。让我们用生产者线程和消费者线程来解释这两种情况。
这两种情况是:
- 消费者线程比生产者线程工作得更快
在这种情况下,数据读取速度较快,但计算机期望的数据更改并未实现。
例子:
今天我们刚刚把两千二百二十二卢比(2,222)存入银行账户。当天存钱六个小时后,我们想从银行提取两千卢比(2,000)。在存入两千二百二十二卢比之前,我们的账户余额为一千五百卢比。
现在,由于生产者线程较慢,账户余额没有更新到三千七百二十二卢比(1500 + 2222 = 3722)。
现在账户余额仅为一千五百卢比。因此,如果我们提取现金,这将不会被更新,我们无法从银行提取两千卢比。由于消费者线程比生产者线程更快的情况,我们面临了这个问题。
- 生产者线程比消费者线程工作得更快
在这种情况下,我们正在计算机中读取数据,突然在读取数据的过程中数据的值发生了改变。
关键区问题中的竞态条件
在进入问题之前,让我们先讨论一下关键区域。
关键区域
关键区问题是一个代码片段。这个代码片段包含了几个变量。这些变量可以被几个进程访问。对于这些进程有一个条件。
这个条件是只有一个进程可以进入关键区。其他想要进入关键区的进程必须等待该进程完成工作,然后才能进入关键区。
一个代码片段中被多个线程执行的部分被认为是关键区。由于同时运行的不同线程产生的各种输出可能导致不同的执行顺序,因此关键区域容易受到竞争条件的影响。
关键区域表示
在操作系统中预防竞态条件
在 Java 中预防竞态条件
我们有一些特定的关键字可以非常有效地避免操作系统中的竞态条件。它们是:
1) volatile 关键字
定义:
使用 volatile 关键字可以让多个线程改变一个变量的值。使类线程安全是另一个应用。它表明多个线程使用一个方法或一个类的实例并不成问题。原始类型和对象都与 volatile 关键字兼容。
volatile 关键字总是从主存中读取变量的值,而不是缓存它。类和方法不能与 volatile 关键字结合使用。虽然它只用于变量,但它确保了顺序和可见性。它阻止编译器重新排列代码。
在竞态条件中的应用
如果变量有 volatile 关键字,则线程不会在本地内存中创建变量的副本。为了防止竞态条件,在某个时间点,同时运行的线程避免报告同一变量的不同值。
2) synchronized 关键字
定义:
使用 synchronized 关键字有许多应用程序:
静态方法
代码块
实例方法
当我们使用 synchronized 块时,Java 采用一个监视器,通常称为监视器锁或内部锁,以确保同步。由于这些监视器与对象相关联,因此同一对象的所有同步块只能同时运行一个线程。
在竞态条件中的应用
通过使用 synchronized 关键字将其包围,共享资源或代码块只能被一个线程访问。
在外部介质中预防竞态条件
1) 数据提取
竞态条件可能以各种方式在网络、存储和内存中显现。积极地监测和预防它们是软件和技术设计和开发的重要组成部分。
由于黑客可能利用竞争条件漏洞来未经授权地访问网络,因此预防竞争情况至关重要。Linux 内核的内存子系统错误被用于基于竞争条件的著名攻击 Dirty Cow 中,使攻击者可以获得对只读内存映射的写入权限。
2) 网络
如果两个用户在网络上尝试同时访问一个通道,并且在系统允许访问之前,没有一个计算机收到通道已经在使用的警告,那么可能会出现竞争条件。据统计,使用地球同步卫星或具有其他长延迟时间的网络更可能遇到此类问题。
必须开发优先级机制来授予一个用户独占访问权限,以避免这种竞争情况。当两个订户在规定的时间内尝试访问系统时,登录或数字以较早字母开头或数字较低的订户可能会获得优先权。