(学习笔记-进程管理)多线程冲突如何解决

这篇具有很好参考价值的文章主要介绍了(学习笔记-进程管理)多线程冲突如何解决。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

对于共享资源,如果没有上锁,在多线程的环境里,很有可能发生翻车。


竞争与合作

在单核 CPU 系统里,为了实现多个程序同时运行的假象,操作系统通常以时间片调度的方式,让每个进程每次执行一个时间片,时间片用完了,就切换下一个进程运行,由于这个时间片的时间很短,于是就造成了 并发 现象

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 另外,操作系统也为每个进程创建巨大,私有的虚拟内存的假象,这种地址空间的抽象,让每个程序好像拥有自己的内存,而实际上操作系统在背后秘密地让多个地址空间 复用 物理内存或磁盘。

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

如果一个程序只有一个执行流程,也代表它是单线程的。当然一个程序可以有多个执行流程,也就是所谓的多线程程序,线程是调度的基本单位,进程则是资源分配的基本单位。

所以,线程之间是可以共享进程的资源,比如代码段、堆空间、数据段、打开的文件等资源、但每个线程都有自己独立的栈空间。

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 那么问题就来了,多个线程如果竞争共享资源,如果不采取有效的措施,则会造成共享数据的混乱。

实验:创建两个线程,它们分别对共享变量 i 自增 1 执行 10000 次,如下代码:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 按理来说, i 变量最后应该是 20000 ,但是,实际结果如下:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 运行了两次,发现结果 i 值可能会20000,也可能为其他结果。

每次运行不但会产生错误,而且得到不同的结果。在计算机里是不能容忍的,虽然是小概率出现的错误,但是小概率事件它一定是会发生的。

为什么会出现这种情况?

为了理解为什么会发生这种情况,必须了解编译器为更新计数器 i 变量生成的代码序列,也就是要了解汇编指令的执行顺序。

在这个例子中,我们只是想给 i 加上数字 1 ,那么它对应的汇编指令执行的过程是这样的:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 可以发现,只是单纯给 i 加上数字 1,在 CPU 运行的时候,实际上要执行 3 条指令。

假设线程 1 进入这个代码区域,它将 i 的值(假设此时为 50 ) 从内存加载到它的寄存器中,然后它向寄存器加 1 ,此时在寄存器中的 i 的值是 51 。

现在,不幸的事情发生了:时钟中断发生。因此,操作系统将当前正在运行的线程的状态保存到线程的线程控制块 TCB。

现在更糟糕的事情发生了:线程 2 被调度运行,并进入同一段代码。它也执行了第一条指令,从内存获取 i 值并将其放入到寄存器中,此时内存中的 i 的值仍为 50,因此线程2寄存器中的 i 值也是 50。假设线程 2 执行接下来的两条指令,将寄存器中的 i 值 +1,然后将寄存器中的 i 值保存到内存中,于是此时全局变量 i 值是 51。

最后,又发生了一次上下文切换,线程 1 恢复执行。还记得它已经执行了两条汇编指令,现在准备执行后一条指令。在前面,线程 1 寄存器中的 i值为51,因此执行最后一条指令后,将值保存到内存,全局变量 i 的值再次被设置为 51.

简单来说,增加 i(值为50)的代码被运行两次,按理来说,最后的 i值应该是 52 ,但是由于不可控的调度,导致最后 i 值为 51.

针对2上面线程 1 和 线程 2 的执行过程,可以表示为:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 互斥

上面展示的情况称为竞争条件,当多线程相互竞争操作共享变量时,由于运气不好,即在执行过程中发生了上下文切换,我们得到了错误的结果,事实上,每次运行都可能得到不同的结果,因此输出的结果存在不确定性

由于多线程执行操作共享变量的这段代码可能会导致竞争状态,因此我们将此段代码称为临界区,它是访问共享资源的代码片段,一定不能给多线程同时执行

我们希望这段代码是互斥的,也就是说保证一个线程在临界区执行时,其他线程应该被阻止进入临界区,即这段代码执行过程中,最多只能出现一个线程。                                                                 

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 另外,互斥并不是只针对多线程。在多线程竞争共享资源的时候,也同样是可以使用互斥的方式来避免资源竞争造成的资源混乱。

同步

互斥解决了并发进程/线程对临界区的使用问题。这种基于临界区控制的交互作用是比较简单的,只要一个进程/线程加入了临界区,其他试图想进入临界区的进程/线程都会被阻塞,直到第一个进程/线程离开了临界区。

在多线程里,每个线程并不一定是顺序执行的,它们基本是以各自独立的、不可预知的速度向前推进,但是有时候我们又希望多个线程能密切合作,以实现一个共同的任务

例子,线程 1 是负责读入数据的,而线程 2 是负责处理数据的,这两个线程是相互合作、相互依赖的。线程 2 在没有收到线程 1 的唤醒通知时,就会一直阻塞等待,当线程 1 读完数据需要把数据传给线程 2 时,线程 1 会唤醒线程 2,并把数据交给线程 2 处理。

所谓同步,就是并发进程/线程在一些关键点上可能需要互相等待与互通消息,这种互相制约的等待和互通信息称为进程/线程同步

PS:同步和互斥是两种不同的概念:

  • 同步: [操作A应在操作B之前执行],[操作C必须在操作A和操作B都完成后才能执行] 等
  • 互斥: [操作A和操作B不能在同一时刻执行];

互斥与同步的实现和使用

在进程/线程并发执行的过程中,进程/线程之间存在协作的关系,例如有互斥、同步的关系。

为了实现进程/线程间正确的协作,操作系统必须提供实现进程协作的措施和方法,主要方法有两种:

  • :加锁、解锁操作
  • 信号量:P、V操作

这两个都可以方便地实现进程/线程互斥、而信号量比锁的功能更强一些,它还能方便地实现进程/线程同步。

使用加锁操作和解锁操作可以解决并发线程/进程的互斥问题。

任何想进入临界区的线程,必须先执行加锁操作。若加锁操作顺利通过,则线程可进入临界区;在完成对临界资源的访问后再执行解锁操作,以释放该临界资源。

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 根据锁的实现不同,可以分为 [忙等待锁] 和 [无忙等待锁]。

忙等待锁 的实现

在说明 忙等待锁 的实现之前,需要先了解一下现代CPU体系结构提供的特殊原子操作指令 -- 测试和置位(Test-and-Set)指令

如果用 C 代码表示 Test-and-Set指令,形式如下:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

测试并设置指令做了下述事情:

  • 把 old_ptr 更新为 new 的新值
  • 返回 old_ptr 的旧值

关键的是这些代码是原子执行的。因为既可以测试旧值,又可以设置新值,所以我们把这条指令叫做 [测试并设置]。

原子操作:要么全部执行,要么都不执行,不能出现执行到一半的中间状态

我们可以运用Test-and-Set指令来实现 [忙等待锁],代码如下:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 我们来理解为什么这个锁能够工作:

  • 场景一:首先假设一个线程正在运行,调用 lock() ,没有其他线程持有锁,所以 flag 是 0。当调用 TestAndSet(flag , 1) 方法,返回 0,线程会跳出 while 循环,获取锁。同时也会原子的设置 flag为 1 ,标志锁已经被持有。当线程离开临界区,调用 unlock() flag 清理为 0.
  • 场景二:当某个线程已经持有锁(即 flag 为 1)。本线程调用 lock(),然后调用 TestAndSet(flag , 1) ,这一次返回 1 ,只要另一个线程一直持有锁,TestAndSet() 会重复返回 1 ,本线程会一直忙等。当 flag 终于被改为 0,本线程会调用 TestAndSet() ,返回 0 并且原子地设置为 1 ,从而获得锁,进入临界区。

很明显,当获取不到锁时,线程会一直 while 循环,不做任何事情,所以就被称为 [忙等待锁],也被称为 自旋锁

这是最简单的一种锁,一直自旋,利用CPU周期,直到锁可用。在单处理器上,需要抢占式的调度器(即不断通过时钟中断一个线程,运行其他线程。)否则,自旋锁在单CPU上无法使用,因为一个自旋的线程永远不会放弃CPU。

无等待锁 的实现

无等待锁:获取不到锁的时候,不用自旋。当没有获取到锁的时候,就把当前线程放入到锁的等待队列,然后执行调度程序,把CPU让给其他线程执行

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

本次只是提出了两种简单锁的实现方式。当然,在具体操作系统中,会更复杂,但也离不开本例中两个基本元素。

信号量

信号量是操作系统提供的一种协调共享资源访问的方法。

通常信号量表示资源的数量,对应的变量是一个整型(sem)变量。

另外,还有两个原子操作的系统调用函数来控制信号的,分别是:

  • P操作:将 sem 1 ,相减后,如果 sem < 0 ,则进程/线程进入阻塞等待,否则继续,表明 P 操作可能会阻塞;
  • V操作:将 sem 1 ,相加后,如果 sem <= 0,唤醒一个等待中的进程/线程,表明 V 操作不会阻塞;

TIP  为什么V 操作中是sem <= 0

例子:如果 sem = 1 ,有三个线程进行了 P 操作:

  • 第一个线程 P 操作后 ,sem = 0;第一个线程继续运行
  • 第二个线程 P 操作后,sem = -1;sem < 0 第二个线程阻塞等待
  • 第三个线程 P 操作后,sem = -2;sem < 0 第三个线程阻塞等待

这时,第一个线程在执行 V 操作后,sem=-1;sem <=0,所以要唤醒阻塞等待中的第二个线程或第三个线程。

P操作是在进入临界区之前,V操作是作用在离开临界区之后,这两个操作必须是成对出现的。

举个类比,2 个资源的信号量,相当于 2 条火车轨道,PV 操作如下图过程:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 操作系统是如何实现PV操作的呢?

信号量数据结构与PV操作的算法描述如下图:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

PV 操作的函数是由操作系统管理和实现的,所以操作系统已经使得执行 PV 函数时是具有原子性的。

PV操作是如何使用的呢?

信号量不仅可以实现临界区的互斥访问控制,还可以线程间的事件同步

信号量实现临界区的互斥访问

为每类共享资源设置一个信号量 s ,其初始值为 1 ,表示该临界资源未被占用。

只要把进入临界区的操作置于 P(s) V(s) 之间,即可实现进程/线程互斥:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 此时,任何想进入临界区的线程,必先在互斥信号量上执行 P 操作,在完成对临界资源的访问后再执行 V 操作。由于互斥信号量的初始值为 1 ,故在第一个线程执行 P 操作后 s 值变为 0,表示临界资源为空闲,可分配给该线程,使之进入临界区。

若此时又有第二个线程想进入临界区,也应先执行 P 操作,结果使 s 变为负值,这就意味着临界资源已被占用,因此第二个线程被阻塞。

并且,直到第一个线程执行 V 操作,释放临界资源而恢复 s 值为 0 后,才唤醒第二个线程,使之进入临界区,待它完成临界资源的访问后,又执行 V 操作,使 s 恢复到初始值 1 。

对于两个并发线程,互斥信号量的值仅取 1、0和-1三个值,分别表示:

  • 如果互斥信号量为 1,表示没有线程进入临界区
  • 如果互斥信号量为 0,表示有一个线程进入临界区
  • 如果互斥信号量为 -1,表示一个线程进入临界区,另一个线程等待进入

通过互斥信号量,就能保证临界区任何时刻只有一个线程在执行,就达到了互斥的效果。

信号量实现事件同步​​​​​​​

同步的方式是设置一个信号量,其初值为 0

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 妈妈一开始询问儿子要不要做饭时,执行的是 P(s1) ,相当于询问儿子需不需要吃饭,由于 s1 初始值为 0,此时 s1 变成 -1,表明儿子不需要吃饭,所以妈妈线程就进入等待状态。

当儿子肚子饿时,执行了 V(s1),使得 s1 信号量从 -1 变成 0,表明此时儿子需要吃饭了,于是就唤醒了阻塞中的妈妈线程,妈妈线程就开始做饭。

接着,儿子线程执行了 P(s2),相当于询问妈妈饭做完了吗,由于 s2 初始值是 0,则此时 s2 变成 -1,说明妈妈还没做完饭,儿子线程就等待状态。

最后,妈妈终于做完饭了,于是执行 V(s2)s2 信号量从 -1 变回了 0,于是就唤醒等待中的儿子线程,唤醒后,儿子线程就可以进行吃饭了

生产者-消费者问题

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 生产者-消费者问题描述:

  • 生产者在生成数据后,放在一个缓冲区中
  • 消费者从缓冲区取出数据处理
  • 任何时刻,只能有一个生产者或消费者可以访问缓冲区

对问题分析可以得出:

  • 任何时刻只能有一个线程操作缓冲区,说明操作缓冲区的是临界代码,需要互斥
  • 缓冲区空时,消费者必须等待生产者生成数据;缓冲区满时,生产者必须等待消费者取出数据。说明生产者和消费者需要同步

那么我们需要三个信号量,分别是:

  • 互斥信号量 mutex :用于互斥访问缓冲区,初始化值为 1;
  • 资源信号量 fullBuffers :用于消费者询问缓冲区是否有数据,有数据则读取数据,初始值为 0(表明缓冲区一开始为空);
  • 资源信号量 emptyBuffers :用于生产者询问缓冲区是否有空位,有空位则生成数据,初始化值为 n(缓冲区大小);

具体实现代码:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 如果消费者线程一开始执行 P(fullBuffers),由于信号量 fullBuffers 初始值为 0,则此时 fullBuffers 的值从 0 变为 -1,说明缓冲区里没有数据,消费者只能等待。

接着,轮到生产者执行 P(fullBuffers),表示减少 1 个空槽,如果当前没有其他生产者线程在临界区执行代码,那么生产者线程就可以把数据放到缓冲区,放完后,执行V(fullBuffers),信号量 fullBuffers 从 -1 变成 0,表明有 消费者 线程正在阻塞等待数据,于是阻塞等待的消费者线程会被唤醒。

消费者线程被唤醒后,如果此时还没有其他消费者线程在读数据,那么就可以直接进入临界区,从缓冲区读取数据。最后,离开临界区,把空槽的个数 + 1。


经典同步问题

哲学家就餐问题

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 哲学家问题描述:

  • 5 个哲学家,围绕着一张圆桌吃面
  • 这个桌子只有 5 只叉子,每两个哲学家之间放一个叉子
  • 哲学家未在一起思考,思考途中饿了就会想进餐
  • 但是,这些哲学家需要两只叉子才愿意吃面,也就是需要拿到左右两边的叉子才能进餐
  • 吃完后,会把叉子放回原处

问题:如何保证哲学家们的动作有序进行,而不会出现有人永远拿不到叉子呢?

方案一

我们用信号量的方式,也就是PV操作来尝试解决:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 上面的程序,看似很自然:拿起叉子用P操作,代表有叉子就直接用,没有叉子时就等待其他哲学家放回叉子。

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 不过这种解法存在一个极端的问题:假设五位哲学家同时拿起左边的叉子,桌面上就没有叉子了,这样就没有人能够拿到它们右边的叉子,也就是说每一位哲学家都会在 P(fork[(i+1)%N])这条语句阻塞了,很明显就发生了死锁的现象

方案二

既然 方案一 会发生同时竞争左边叉子导致死锁的现象,那么我们就在拿叉子前,加个互斥信号量,代码如下:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 上面的程序中互斥信号量的作用在于,只要有一个哲学家进入了临界区,也就是准备要拿叉子时,其他哲学家都不能动,只有这位哲学家用完叉子了,才能轮到下一个哲学家进餐

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

方案二虽然能让哲学家们按顺序吃饭,但是每次进餐只能有一位哲学家,而桌面上是有 5 把叉子,按道理是可以有两个哲学家同时进餐的,所以从效率角度上,这不是最好的解决方案。

方案三

方案一的问题在于,会出现所有哲学家同时拿左边刀叉的可能性,那我们就避免哲学家可以同时拿到左边的刀叉,采用分支结构,根据哲学家的编号不同,采取不同的动作。

即让偶数编号的哲学家 [先拿左边的叉子后拿右边的叉子],奇数编号的哲学家 [先拿右边的叉子后拿左边的叉子]

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 上面的程序,在P操作时,根据哲学家的编号不同,拿起左右两边叉子的顺序不同。另外,V操作是不需要分支的,因为V操作是不会阻塞的。

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 方案三即不会出现死锁又可以两个人同时进餐

方案四

这里再提出一种可行的解决方案,用一个数组state来记录每一位哲学家的三个状态,分别是在进餐状态、思考状态、饥饿状态(正在试图拿叉子)

那么当一个哲学家只有在两个邻居都没有进餐时,才可以进入进餐状态

第 i 个哲学家的左邻右舍,则由宏LEFT和RIGHT定义:

  • LEFT:(i+5-1) % 5
  • RIGHT:(i+1) % 5

比如 i 为2,则 LEFT 为 1 , RIGHT 为 3.

具体实现代码如下:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 上面的程序使用了一个信号量数组,每个信号量对应一位哲学家,这样在所需的叉子被占用时,想进餐的哲学家就被阻塞。

注意,每个进程/线程将 smart_person 函数作为主代码运行,而其他 take_forksput_forks 和 test 只是普通的函数,而非单独的进程/线程。

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 读者-写者问题

前面的「哲学家进餐问题」对于互斥访问有限的竞争问题(如 I/O 设备)一类的建模过程十分有用。

另外,还有个著名的问题是「读者-写者」,它为数据库访问建立了一个模型。

读者只会读取数据,不会修改数据,而写者即可以读也可以修改数据。

读者-写者的问题描述:

  • 「读-读」允许:同一时刻,允许多个读者同时读
  • 「读-写」互斥:没有写者时读者才能读,没有读者时写者才能写
  • 「写-写」互斥:没有其他写者时,写者才能写

方案一

使用信号量的方式来解决问题:

  • 信号量 wMutex:控制写操作的互斥信号量,初始值为 1 ;
  • 读者计数 rCount:正在进行读操作的读者个数,初始化为 0;
  • 信号量 rCountMutex:控制对 rCount 读者计数器点的互斥修改,初始值为 1;

代码的实现:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 上面这种的实现,是读者优先的策略,因为只要有读者正在读的状态,后来的读者都可以直接进入,如果读者持续不断进入,则写者就会处于饥饿状态。

方案二

既然有读者优先策略,自然也有写者优先策略:

  • 只要有写者准备要写入,写者应尽快执行写操作,后来的读者就必须阻塞
  • 如果有写者持续不断写入,则读者就处于饥饿状态

在方案一的基础上新增以下变量:

  • 信号量 rMutex :控制读者进入的互斥信号量,初始值为 1;
  • 信号量 wDataMutex :控制写者写操作的互斥信号量,初始值为1;
  • 写者计数 wCount :记录写者数量,初始值为 0 ;
  • 信号量 wCountMutex :控制 wCount 互斥修改,初始值为 1;

实现代码如下:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 注意,这里 rMutex 的作用,开始有多个读者读数据,它们全部进入读者队列,此时来了一个写者,执行了 P(rMutex) 之后,后续的读者由于阻塞在 rMutex 上,都不能再进入读者队列,而写者到来,则可以全部进入写者队列,因此保证了写者优先。

同时,第一个写者执行了 P(rMutex) 之后,也不能马上开始写,必须等到所有进入读者队列的读者都执行完读操作,通过 V(wDataMutex) 唤醒写者的写操作。

方案三

既然读者优先策略和写者优先策略都会造成饥饿的现象,那么我们就来实现一下公平策略。

公平策略:

  • 优先级相同;
  • 写者、读者互斥访问;
  • 只能一个写者访问临界区;
  • 可以有多个读者同时访问临界资源;

具体代码实现:

(学习笔记-进程管理)多线程冲突如何解决,操作系统,学习,笔记

 对比方案一的读者优先策略,可以发现,读者优先中只要后续有读者到达,读者就可以进入读者队列,而写者必须等待,直到没有读者到达。

没有读者到达就会导致读者队列为空,即 rCount = 0,此时写者才可以进入临界区执行写操作。

这里 flag 的作用就是阻止读者的这种特殊权限(只要读者到达,就可以进入读者队列)。

比如:开始来了一些读者读数据,它们全部进入读者队列,此时来了一个写者,执行 P(falg) 操作,使得后续到来的读者都阻塞在 flag 上,不能进入读者队列,这会使得读者队列逐渐为空,即 rCount 减为 0。

这个写者也不能立马开始写(因为此时读者队列不为空),会阻塞在信号量 wDataMutex 上,读者队列中的读者全部读取结束后,最后一个读者进程执行 V(wDataMutex),唤醒刚才的写者,写者则继续开始进行写操作。文章来源地址https://www.toymoban.com/news/detail-641201.html

到了这里,关于(学习笔记-进程管理)多线程冲突如何解决的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 操作系统进程线程(一)—进程线程协程区别、多进程多线程、进程调度算法、进程线程通信

    定义上 进程: 资源分配和拥有 的基本单位,是调度的基本单位。 运行一个可执行程序会创建一个或者多个进程;进程就是运行起来的程序 线程:程序 执行 基本单位,轻量级进程。 每个进程中都有唯一的主线程 ,主线程和进程是相互依赖的关系。 协程: 用户态 的轻量级

    2024年02月01日
    浏览(57)
  • JUC并发编程学习笔记(一)认知进程和线程

    进程 一个程序,如QQ.exe,是程序的集合 一个进程往往可以包含多个线程,至少包含一个 java默认有两个线程,GC垃圾回收线程和Main线程 线程:一个进程中的各个功能 java无法真正的开启线程,因为java是运行在虚拟机上的,所以只能通过C++,通过native本地方法调用C++开启线程

    2024年02月06日
    浏览(55)
  • FreeRTOS如何解决访问冲突/线程不安全(临界段、互斥锁、挂起调度、看门人任务)

    在多任务(多线程)系统中,存在一个隐患,那就是多线程的访问(在FreeRTOS中就是任务)。当一个任务A开始访问一个资源(外设、一块内存等),但是A还没有完成访问,B任务运行了,也开始访问,这就会造成数据破坏、错误等问题。 例如: 两个任务试图写入一个液晶显示

    2024年02月07日
    浏览(166)
  • 操作系统进程线程(三)—进程状态、同步互斥、锁、死锁

    原子操作的概念 原子操作就是不可中断的一个或者一系列操作。 原子操作如何实现 总线锁定 使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号的时候,其他处理器的请求将被阻塞住,那么该处理器可以独占内存。 缓存锁 总线锁开销比较大,因为把CPU和内

    2024年02月04日
    浏览(51)
  • 【操作系统——进程与线程(一)】

    2.1.1 进程的概念和特征 进程是指正在执行中的程序的实例。它是计算机系统进行资源分配和调度的基本单位。每个进程都有自己的地址空间、堆栈和数据区域,以及与其他进程通信和同步所需要的操作系统资源。 进程具有以下特点: 独立性:进程是独立的执行实体,拥有自

    2024年02月11日
    浏览(55)
  • 操作系统-进程和线程-同步、互斥、死锁

    目录 一、同步互斥  二、互斥的实现方法 2.1软件实现 2.1.1单标志法 2.1.2双标志先检查 2.1.3双标志后检查 2.1.4Petersons算法 2.2硬件实现 2.2.1 TestAndSet指令 2.2.2 Swap指令   三、信号量机制 3.1整形变量  3.2 记录型变量  3.3用信号量实现进程互斥、同步、前驱关系 3.3.1互斥  3.3.2同步

    2024年02月08日
    浏览(52)
  • (学习笔记-进程管理)进程

    我们编写的代码只是一个存储在硬盘的静态文件,通过编译后会生成二进制可执行文件,当我们运行这个可执行文件后,它会被装载到内存中,接着CPU会执行程序中的每一条指令,那么这个 运行中的程序就被称为进程 。 现在我们考虑有一个会读取硬盘文件数据的程序执行了

    2024年02月14日
    浏览(41)
  • 【操作系统学习笔记】文件管理1.5

    参考书籍: 王道考研 视频地址: Bilibili 逻辑结构: 从用户角度看,由创建文件的用户自己设计的 无结构文件 有结构文件 顺序文件 顺序存储 链式存储 索引文件 索引顺序文件 物理结构: 从操作系统看,由操作系统决定 连续分配 链接分配 索引分配

    2024年03月09日
    浏览(66)
  • (学习笔记-进程管理)进程间有哪些通信方式?

    每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间时每个进程都共享的,所以进程之间要通信必须通过内核   在Linux命令中 [ | ]  这个竖线就是一个 管道 。 它的功能是讲前一个命令(ps auxf)的输出,作为后一个命令(grep mysql)的输入,从这功

    2024年02月13日
    浏览(50)
  • (学习笔记-进程管理)怎么避免死锁?

    在多线程编程中,我们为了防止多线程竞争共享资源而导致数据错乱,都会在操作共享资源之前加上互斥锁,只有成功获得到锁的线程,才能操作共享资源,获取不到锁的线程就只能等待,直到锁被释放。 那么,当两个线程为了保护两个不同的共享资源而使用了两个互斥锁,

    2024年02月12日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包