博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Linux]竞态→互斥机制(中断屏蔽、原子操作、自旋锁、信号量)
阅读量:3920 次
发布时间:2019-05-23

本文共 3387 字,大约阅读时间需要 11 分钟。

竞态初步引入

(1)竞态全称是:竞争状态,多进程环境下,多个进程同时抢占系统资源(内存、CPU运行时间、文件IO)

(2)竞争状态对OS来说是很危险的,此时OS如果没处理好就会造成结果不确定。
(3)写程序当然不希望程序运行的结果不确定,所以我们写程序时要尽量消灭竞争状态。操作系统给我们提供了一系列的消灭竟态的机制,我们需要做的是在合适的地方使用合适的方法来消灭竟态。

同步与互斥

  互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

  同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

Linux提供的同步机制:信号量、文件锁(文件记录锁和文件锁)、互斥量、条件变量。其中后两者需要依赖于共享内存才能用于进程间同步,因此只有文件锁是进程生存期的资源,其他的都属于内核生存期资源。除此之外,信号也可用于进程同步。

互斥基本概念

互斥机制

  访问
共享资源的代码叫做
临界区【每次只允许一个进程进入临界区,进入后不允许其他进程进入】。共享资源被多个线程需要,但共享资源又不能被同时访问。 所以临界区需要以某种
互斥机制来加以保护,确保共享资源被互斥访问。

用户空间和内核空间

  为了安全考虑,
Linux系统分为内核态和用户态,分别运行在内核空间和用户空间。内核态的程序可以执行【特权指令】,操作系统本身也在其中运行;用户态则不允许直接访问操作系统的核心数据、设备等关键资源,必须先通过
系统调用或者中断进入内核态才可以访问,当系统调用或中断返回时,重新回到用户空间运行。

Linux 的互斥机制

四种方式:中断屏蔽、原子操作、自旋锁、信号量

内核空间互斥方式:中断屏蔽、原子操作、自旋锁

用户空间互斥方式:信号量

朱有鹏3.7线程同步角度:互斥量、条件变量、信号量https://blog.csdn.net/qq_42024067/article/details/99844580

中断屏蔽

  中断是一个完全异步的事件,它的发生与正在运行的进程没有任何关系,它
没有进程上下文切换

  CPU具备屏蔽中断和打开中断的功能,这项功能可以保证正在执行的内核执行路径不被中断处理程序抢占,防止竞态的产生。 但是,内核的正常运行依赖于中断机制。在屏蔽中断期间,任何中断都无法得到处理,而必须等待屏蔽解除。因此长时间屏蔽中断对内核的运行起到很大的影响,其后果可能导致数据丢失,甚至系统崩溃。

实际情况是:在中断服务全过程屏蔽中断会丢失中断;如果开中断,又容易引起互斥问题。为了解决这个问题,Linux 把中断分为顶半部TH(Top Half)和底半部BH(Bottom Half)。TH 屏蔽中断,执行一些少量的关键性动作;BH 可以开中断,允许中断延迟执行。

原子操作

原子操作底层表现为一条汇编指令(ldrex、strex)。所以他们在执行过程中不会被别的代码路径所中断。

Linux 内核提供了两类函数来实现内核中的原子操作,分别是整型原子操作位原子操作。它们的共同点是所有的操作都是原子的,内核可以安全的调用它们而不被中断,而且它们都依赖底层CPU的原子操作实现,因此所有的这些函数都是与CPU架构相关的。

自旋锁

自旋锁是为实现保护共享资源而提出一种锁机制。

自旋锁的原理:

  一个执行单元要想访问被自旋锁保护的共享资源,必须先得到锁,并且在任何时刻最多只能有一个执行单元获得锁; 而在访问完共享资源后,必须释放锁。

自旋锁的特点

  自旋锁使用者一般保持锁时间是非常短,所以某些情况下,选择自旋而不是睡眠是很有必要的,自旋锁的效率远高于互斥锁。
如果被保护的共享资源需要在中断上下文访问,就必须使用自旋锁。
在单cpu且不可抢占的内核下,自旋锁的所有操作都是空操作。
  事实上,自旋锁的初衷是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁被持有的时间不应该过长。如果需要长时间锁定的话, 最好使用信号量。

相同:如果在获取锁时,没有任何执行单元保持该锁,那么将立即得到锁(能够进入临界区访问公共临界资源);差异:当锁已经被别人拿走时,  ①该自旋锁的请求调用者会一直循环并测试该自旋锁的保持者是否已经释放了锁【忙等,占CPU】,   直到该自旋锁的保持者释放了锁,所以才叫自旋。[请求者不会阻塞]  ②互斥锁:请求调用者者会阻塞【放弃占CPU】

信号量

在用户空间只有进程的概念。当一个临界区有多个用户态进程竞争时,最好的方法是用信号量保护这个临界区。

只有得到信号量进程才能执行临界区代码,当获取不到信号量时,进程进入休眠状态。

因此,我们可以说,信号量是进程级的互斥机制【当然也能用于线程】,它代表进程来争夺共享资源,如果竞争失败,就会发生进程上下文切换,当前进程进入睡眠状态,CPU运行其他进程。由于进程上下文切换的开销很大,因此,只有当进程占用资源时间较长时,用信号量才是最好的选择。

此外,信号量在SMP(对称多处理器)系统同样起作用。

浅显的比方

浅显的来说,可以理解为大家在一套房子里合租,共用一个厕所。厕所就是共享资源,去上厕所的行为被称作代码路径。

中断屏蔽就是,有一个人想要用厕所,但是呢他在上厕所前在门口贴上纸条说厕所坏了,如果他很快出来倒还不要紧,但是如果他要上很长时间,那一起住的其他人可能就要憋爆了。所以中断屏蔽最开始不会被用于处理需要耗时很长的操作。但是大家想,这样不是个解决办法啊,我有时候确实要拉很长时间怎么办呢。
于是发明了顶半部TH和底半部BH,TH用于执行少量的关键性的动作,BH用于处理中断中耗时的部分。
可以理解为,某人A特别特别想上厕所的时候,就进入TH(可以看作一个状态),此时A去应个急,拉一点点,让肚子不那么疼,此时A是不可以被打断的。如果没人用厕所他就直接慢条斯理的开始拉了(BH)。但是他这个BH状态是可以打断的,如果此时来个人B 非常非常急,B进入TH说,我受不了啦要憋死啦,A就会暂停自己的状态(保护现场)让B进来拉一会(TH),等B拉了一点点,让肚子不那么疼了就出去。此时 A继续(恢复现场)。等A 的BH部分完全结束后B再执行B的BH部分。
原子操作很好理解,就是大家每次上厕所都用时非常短,短到什么程度呢,只要一条汇编指令的时间。当然拉的量也非常少(只改变一个整型或者是位)。所以就不存在抢厕所的问题了。
自旋锁顾名思义,给这个厕所上把锁,只有拥有这个锁钥匙的人A才能进厕所。进去后把锁锁上,外面的人B急得团团转(自旋),出来后把锁释放,在门口等着的B拿了钥匙赶紧开了锁进去了。但是缺点就是,B在外面团团转,没有功夫去做别的事情,所以一旦 A 上厕所的时间很长,B就浪费了很长时间在自旋上。对系统的性能有所影响。

信号量相关函数

//1.分配信号量对象  struct semaphore sema;//2.初始化为互斥信号量  init_MUTEX(&sema);或者:  DECLARE_MUTEX(sema);//3.访问临界区之前获取信号量  down(&sema);   //如果获取信号量,立即返回  //如果信号量不可用,进程将在此休眠,并且休眠的状态是 [ 不可中断的休眠状态 TASK_UNINTERRUPTIBLE] !  或者  down_interruptible(&sema);  //如果信号量不可用,进程将进入 [ 可中断的休眠状态 TASK_INTERRUPTIBLE ],如果返回0表示正常获取信号,如果返回非0,表示接受到了信号  down_trylock();   //获取信号,如果信号量不可用,返回非0,如果信号量可用,返回0;不会引起休眠,可以在中断上下文使用。返回值也要做判断!//4.访问临界区:临界区可以休眠//5.释放信号量  up(&sema);  //不仅仅释放信号量,然后唤醒休眠的进程,让这个进程去获取信号量来访问临界区
你可能感兴趣的文章
剑指 Offer 31. 栈的压入、弹出序列
查看>>
剑指 Offer 32 - III. 从上到下打印二叉树 III
查看>>
剑指 Offer 33. 二叉搜索树的后序遍历序列
查看>>
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
查看>>
剑指 Offer 68 - II. 二叉树的最近公共祖先
查看>>
剑指 Offer 18. 删除链表的节点
查看>>
剑指 Offer 32 - II. 从上到下打印二叉树 II
查看>>
杭电oj-2011 多项式求和 C++
查看>>
杭电oj-2014 青年歌手大奖赛_评委会打分 C++
查看>>
杭电oj-2015 偶数求和 C++
查看>>
杭电oj-2016 数据的交换输出 C++
查看>>
杭电oj-2017 字符串统计 C++
查看>>
杭电oj-2018 母牛的故事 C++
查看>>
Educational Codeforces Round 87 (Rated for Div. 2)----题目+题解(A、B)
查看>>
Codeforces Round #647 (Div. 2) - Thanks, Algo Muse!B. Johnny and His Hobbies(异或)---题解
查看>>
使用WinINet获取网页源代码
查看>>
Ansi、Unicode、UTF-8字符串之间的转换和写入文本文件
查看>>
error C1189:#error:This file requires _WIN32_WINNT to be #defined at least to 0x0403
查看>>
CentOS yum 源的配置与使用
查看>>
error while loading shared libraries: libevent-2.0.so.5 安装好mamcache,启动服务时
查看>>