嵌入式知识点总结 操作系统 专题提升(三)-并发与互斥
针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。
目录
1.驱动里面为什么要有并发、互斥的控制?如何实现举个例子?
2.自旋锁是什么?信号量是什么?二者有何异同?
3.自旋锁和信号量可以睡眠吗?为什么?
4.自旋锁和信号量可以用于中断中吗?
5.读写锁是什么?
6.产生死锁的原因是什么?
7.死锁的4个必要条件是什么?
8.死锁的处理方式有哪些?
9.如何避免死锁?
10.请问单核机器上写多线程程序,是否需要考虑加锁,为什么?
1.驱动里面为什么要有并发、互斥的控制?如何实现举个例子?
并发,指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态。
解决竟态问题的途径是保证对共享资源的互斥访问,所谓互斥访问就是指一个执行单元在访问共享资源的时候,其他的执行单元都被禁止访问。
访问共享资源的代码区域被称为临界区,临界区需要以某种互斥机制加以保护。中断屏蔽,原子操作自旋锁,和信号量都是linux设备驱动中可采用的互斥途径。
2.自旋锁是什么?信号量是什么?二者有何异同?
自旋锁是一种简单的锁机制,用于保护共享资源。它的基本思想是,如果一个线程想要获取锁,但是锁已经被其他线程持有,它不会被阻塞或挂起,而是“自旋”等待,即不断循环检查锁是否已经释放。自旋锁的特点是:
- 非阻塞:当锁被其他线程持有时,尝试获取锁的线程不会进入休眠状态,而是一直在“忙等”。
- 效率较低:如果锁被持有的时间较长,自旋锁会导致CPU资源的浪费。
- 适合锁持有时间较短的情况:自旋锁适用于锁持有时间较短的场景,避免了线程挂起和唤醒的开销。
信号量是一种更为通用的同步机制,它用于控制多个线程或进程对共享资源的访问。信号量有两种类型:
- 计数信号量:信号量的值表示可用资源的数量。当信号量的值大于0时,线程可以访问共享资源,并且信号量的值会减少;当值为0时,线程会被阻塞,直到资源被释放。
- 二值信号量(Binary Semaphore):它的值只有0和1,类似于互斥锁。一个线程获取信号量后,值变为0,释放时信号量值变为1。
信号量的特点是:
- 阻塞机制:当一个线程请求信号量,但信号量的值为0时,线程会被阻塞,直到其他线程释放信号量。
- 适合锁持有时间较长的情况:信号量的阻塞机制使得它适用于锁持有时间较长的场景,避免了CPU资源的浪费。
特性 | 自旋锁 | 信号量 |
---|---|---|
基本原理 | 线程自旋等待锁释放 | 线程阻塞等待资源可用 |
锁机制 | 非阻塞(自旋) | 阻塞(线程挂起) |
性能 | 适用于锁持有时间短的情况 | 适用于锁持有时间长的情况 |
开销 | 线程会一直占用CPU | 线程在等待时会被挂起,避免了CPU浪费 |
使用场景 | 多核处理器中,锁持有时间较短 | 多核或多进程中,锁持有时间较长 |
实现 | 需要原子操作(如cmpxchg ) | 通过内核提供的信号量API进行管理 |
3.自旋锁和信号量可以睡眠吗?为什么?
自旋锁不能让线程进入睡眠状态。自旋锁的核心机制是线程在获取锁失败时,不会进入休眠或被阻塞,而是会在一个循环中不断检查锁是否被释放,这种行为被称为自旋。因此:
- 自旋锁不会让线程睡眠,即使线程没有获得锁,它也会持续占用CPU资源进行自旋,直到锁被释放。
- 这使得自旋锁适合用于锁持有时间非常短的场景,因为在这种情况下,自旋锁的效率相对较高,不会有上下文切换的开销。
信号量在等待资源时通常会让线程进入睡眠状态。当一个线程请求信号量时,如果信号量的值为0,表示没有可用的资源,此时线程会被阻塞,并进入睡眠状态,直到信号量的值变为大于0,线程被唤醒并继续执行。信号量的行为可以分为两种:
- 计数信号量:当请求信号量的线程无法获得资源时,它会被挂起,直到资源被释放,信号量的值大于0时才会唤醒线程。
- 二值信号量:其行为与互斥锁相似,线程会被挂起,直到信号量的值变为1(即锁被释放)时才会被唤醒。
因此,信号量的特点是:
- 信号量会导致线程睡眠,因为它会阻塞线程,直到资源变得可用。
4.自旋锁和信号量可以用于中断中吗?
信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠。
自旋锁可以用于中断。在获取锁之前一定要先禁止本地中断(也就是本CPU中断,对于多核SOC来说会有多个CPU核),否则可能导致锁死现象的发生。
5.读写锁是什么?
当临界区的一个文件可以被同时读取,但是并不能被同时读和写。如果一个线程在读,另一个线程在写,那么很可能会读取到错误的不完整的数据。读写自旋锁是可以允许对临界区的共享资源进行并发读操作的。但是并不允许多个线程并发读写操作。
6.产生死锁的原因是什么?
多个并发进程因争夺系统资源而产生相互等待的现象。即:一组进程中的每个进程都在等待某个事件发生,而只有这组进程中的其他进程才能触发该事件,这就称这组进程发生了死锁。
产生死锁的本质原因为
1.系统资源有限。
2.进程推进顺序不合理。
7.死锁的4个必要条件是什么?
1.互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2.占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
3.不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来.
4.循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了。
8.死锁的处理方式有哪些?
死锁的处理方式主要从预防死锁、避免死锁、检测与解除死锁这四个方面来进行处理。
预防死锁
1.资源一次性分配:(破坏请求和保持条件)
2.可剥夺资源:即当某进程新的资源未满足时,释放已占有的资源(破坏不可剥夺条件)3.资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
避免死锁
预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则进程等待。其中最具有代表性的避免死锁算法是银行家算法。
检测死锁
首先为每个进程和每个资源指定一个唯一的号码;然后建立资源分配表和进程等待表
解除死锁
当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:
1.剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;2.撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,,死锁状态,消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。
9.如何避免死锁?
1. 死锁预防
死锁预防的目标是通过消除死锁发生的必要条件之一来防止死锁。常见的预防方法包括:
1.1 破坏互斥条件
-
资源共享:有些资源可以被多个进程共享(如只读资源),如果可以让多个进程共享某些资源,就可以避免死锁中的“互斥条件”。但是,并非所有资源都可以共享(如打印机、数据库锁等),因此这个方法在许多场景下并不适用。
1.2 破坏占有且等待条件
-
一次性申请所有资源:要求进程在开始执行之前一次性申请所有需要的资源。如果资源不足,进程会释放所有资源并等待重新申请,而不是在执行过程中逐步申请资源。虽然这种方法可以防止“占有且等待”条件,但可能导致资源的浪费,因为某些资源可能会在某些进程实际使用之前被占用。
1.3 破坏不可抢占条件
-
允许抢占资源:如果一个进程在执行过程中请求到某个资源时,其他进程需要这个资源并且无法获取,可以将资源抢占回来。例如,如果一个进程持有资源A并等待资源B,则系统可以强制将A从进程中取回并分配给等待B的进程,进程再重新申请资源A。这种方式通常适用于资源可以被抢占的情况(例如内存)。
1.4 破坏循环等待条件
-
资源的有序分配:给每种资源分配一个唯一的顺序编号,要求进程按照编号顺序申请资源。如果一个进程需要资源A和资源B,要求它先申请编号较小的资源。例如,如果资源A的编号小于资源B的编号,那么进程必须先申请资源A,再申请资源B。这样可以避免出现环路,防止循环等待的情况。
2. 死锁避免
死锁避免是一种更加动态的策略,系统在资源分配时会检查是否有可能进入死锁状态。死锁避免的关键是 安全状态 的概念,即系统中资源的分配使得进程始终可以在有限的时间内完成,不会陷入死锁。
2.1 银行家算法
银行家算法是一种经典的死锁避免算法,它要求系统在每次分配资源之前,检查该分配是否会导致死锁。算法基于以下假设:
-
每个进程的资源需求是已知的,并且每个进程会在运行期间不断请求资源。
-
系统会检查每次资源分配后,是否仍然可以通过“安全序列”使所有进程完成。如果分配资源会导致系统进入不安全状态,那么该分配请求就会被拒绝。
银行家算法通过模拟资源分配来确保系统始终保持在安全状态。只有在确定资源分配后不会导致死锁时,才会实际进行资源分配。
3. 死锁检测与恢复
死锁检测与恢复方法是通过持续监测系统中是否发生了死锁,一旦发现死锁,就采取措施进行恢复。常见的死锁检测与恢复策略有:
3.1 资源分配图
系统通过建立资源分配图来检测死锁。资源分配图中,节点表示进程和资源,边表示资源的请求和分配。如果图中存在环路,则表示死锁已经发生。
3.2 死锁检测
-
定期检测:系统定期检查资源分配图或进程状态,确定是否存在死锁。如果检测到死锁,系统可以采取措施(如终止某些进程、撤销资源请求等)来恢复。
3.3 死锁恢复
-
进程终止:在检测到死锁后,可以终止一个或多个进程以打破死锁的循环。可以选择终止进程的策略是随机选择终止一个进程,或者选择优先级最低、资源占用最少的进程。
-
资源回收:通过回收进程所占有的资源,将资源释放给其他进程,以避免死锁的发生。
10.请问单核机器上写多线程程序,是否需要考虑加锁,为什么?
在单核机器上写多线程程序,仍然需要线程锁。因为线程锁通常用来实现线程的同步和通信。在单核机器上的多线程程序,仍然存在线程同步的问题。因为在抢占式操作系统中,通常为每个线程分配一个时间片,当某个线程时间片耗尽时,操作系统会将其挂起,然后运行另一个线程。如果这两个线程共享某些数据,不使用线程锁的前提下,可能会导致共享数据修改引起冲突。
你像32跑RTOS一样的嘛。。