【干货】同步与互斥的失败例子
韦东山老师最新录制的驱动大全之<<同步与互斥>>收费视频已经在淘宝上架销售 ,一共7节,良心价29元,同时已经同步到CSDN , 51CTO , 电子发烧友,腾讯课堂等平台。
本文是其中一节《同步与互斥的失败例子》视频配套文档,
《同步与互斥的失败例子》免费试看版(2分钟,完整版7分46秒):
《同步与互斥的失败例子》完整版 购买链接:
https://item.taobao.com/item.htm?id=620987021249 (复制到浏览器打开)
光看文档可能难度稍大,建议结合视频进行学习;gg完毕,开始干货。
1.2 同步与互斥的失败例子
注意:本节在GIT上没有源码。
GIT地址:
git clone https://e.coding.net/weidongshan/01_all_series_quickstart.git
一句话理解同步与互斥:我等你用完厕所,我再用厕所。什么叫同步?就是条件不允许,我要等等。什么是互斥?你我早起都要用厕所,谁先抢到谁先用,中途不被打扰。
同步与互斥经常放在一起讲,是因为它们之间的关系很大,“互斥”操作可以使用“同步”来实现。我“等”你用完厕所,我再用厕所。这不就是用“同步”来实现“互斥”吗?有时候看代码更容易理解,伪代码如下:
01 void 抢厕所(void)
02 {
03 if (有人在用) 我眯一会;
04 用厕所;
05 喂,醒醒,有人要用厕所吗;
06 }
假设有A、B两人早起抢厕所,A先行一步占用了;B慢了一步,于是就眯一会;当A用完后叫醒B,B也就愉快地上厕所了。
在这个过程中,A、B是互斥地访问“厕所”,“厕所”被称之为临界资源。我们使用了“休眠-唤醒”的同步机制实现了“临界资源”的“互斥访问”。
上面是一个有“味道”的例子,回到程序员的世界,一个驱动程序同时只能有一个APP使用,怎么实现?
1.2.1 失败例子1
01 static int valid = 1;
02
03 static ssize_t gpio_key_drv_open (struct inode *node, struct file *file)
04 {
05 if (!valid)
06 {
07 return -EBUSY;
08 }
09 else
10 {
11 valid = 0;
12 }
13
14 return 0; //成功
15 }
16
17 static int gpio_key_drv_close (struct inode *node, struct file *file)
18 {
19 valid = 1;
20 return 0;
21 }
22
看第5行,我们使用一个全局变量valid来实现互斥访问。这有问题吗?很大概率没问题,但是并非万无一失。
注意:编写驱动程序时,要有系统的概念,程序A调用驱动程序时,它可能被程序B打断,程序B也去调用这个驱动程序。下图是一个例子,程序A在调用驱动程序的中途被程序B抢占了CPU资源:
程序A执行到第11行之前,被程序B抢占了,这时valid尚未被改成0;程序B调用gpio_key_drv_open时,发现valid等于1,所以成功返回0;当程序A继续从第11行执行时,它最终也成功返回0;这样程序A、B都成功打开了驱动程序。
注意:在内核态,程序A不是主动去休眠、主动放弃CPU资源;而是被优先级更高的程序B抢占了,这种行为被称为“preempt”(抢占)。
1.2.2 失败例子2
上面的例子是不是第5行到第11行的时间跨度大长了?再优化一下程序行不行?代码如下:
01 static int valid = 1;
02
03 static ssize_t gpio_key_drv_open (struct inode *node, struct file *file)
04 {
05 if (--valid)
06 {
07 valid++;
08 return -EBUSY;
09 }
10 return 0;
11 }
12
13 static int gpio_key_drv_close (struct inode *node, struct file *file)
14 {
15 valid = 1;
16 return 0;
17 }
18
第5行先减1再判断,这样可以更大概率地避免问题,但是还是不能确保万无一失。对数据的修改分为3步:读出来、修改、写进去。请看下图:
进程A在读出valid时发现它是1,减1后为0,这时if不成立;但是修改后的值尚未写回内存;假设这时被程序B抢占,程序B读出valid仍为1,减1后为0,这时if不成立,最后成功返回;轮到A继续执行,它把0值写到valid变量,最后也成功返回。这样程序A、B都成功打开了驱动程序。
1.2.3 失败例子3
前面2个例子,都是在修改valid的过程中被别的进程抢占了,那么在修改valid的时候直接关中断不就可以了吗?
01 static int valid = 1;
02
03 static ssize_t gpio_key_drv_open (struct inode *node, struct file *file)
04 {
05 unsigned long flags;
06 raw_local_irq_save(flags); // 关中断
07 if (--valid)
08 {
09 valid++;
10 raw_local_irq_restore(flags); // 恢复之前的状态
11 return -EBUSY;
12 }
13 raw_local_irq_restore(flags); // 恢复之前的状态
14 return 0;
15 }
16
17 static int gpio_key_drv_close (struct inode *node, struct file *file)
18 {
19 valid = 1;
20 return 0;
21 }
第06行直接关中断,这样别的线程、中断都不能来打扰本线程了,在它读取、修改valid变量的过程中无人打扰。没有问题了?
对于单CPU核的系统上述代码是没问题的;但是对于SMP系统,你只能关闭当前CPU核的中断,别的CPU核还可以运行程序,它们也可以来执行这个函数,同样导致问题,如下图:
假设CPU 0上进程A、CPU1上进程B同时运行到上图中读出valid的地方,它们同时发现valid都是1,减减后都等于0,在第07行判断条件都不成立,所以在第14行都可以返回0,都可以成功打开驱动。
推荐阅读:
专辑|Linux文章汇总
专辑|程序人生
专辑|C语言
嵌入式Linux
微信扫描二维码,关注我的公众号
【干货】同步与互斥的失败例子相关推荐
- 进程的同步、互斥以及PV原语
在处理进程间的同步与互斥问题时,我们离不开信号量和PV原语,使用这两个工具的目的在于打造一段不可分割不可中断的程序.应当注意的是,信号量和PV原语是解决进程间同步与互斥问题的一种机制,但并不是唯一的机 ...
- Linux 线程如何实现同步与互斥
线程安全:多个执行流对临界资源的争抢访问,但是并不会造成数据的二义性 线程的安全主要是通过同步和互斥来实现的 同步:通过条件判断保证对临界资源访问的合理性 互斥:通过同一时间的唯一访问实现对临界资源访 ...
- 进程与线程(同步、互斥、通信方式等)
一.并发 并行 同步 异步 多线程的区别(引用:https://blog.csdn.net/cqkxboy168/article/details/9026205) 1. 并发:在操作系统中,是指一个时 ...
- C++ 线程同步之互斥锁
文章目录 1.简介 2.std::mutex 3.线程同步 4.std::lock_guard 5.std::recursive_mutex-少用 6.std::timed_mutex 1.简介 进行 ...
- Java并发,并行,同步,互斥
2019独角兽企业重金招聘Python工程师标准>>> 一切都要从这个世界的并行性开始说,事物的发展总是并行进行的,汽车在奔驰的同时,自行车也在行驶;别人正在唱歌,你可能正在吃饭;等 ...
- 临界区、事件、互斥量、 信号量--四种控制多线程同步与互斥的方法
// MultiThread.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h"#include <Windows.h> #inclu ...
- 【Linux系统编程】线程同步与互斥:POSIX无名信号量
信号量概述 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问. 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 ...
- 【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)
上篇文章也蛮好,线程同步之条件变量与互斥锁的结合: http://www.cnblogs.com/charlesblc/p/6143397.html 现在有这篇文章: http://blog.csd ...
- linux线程基础篇----线程同步与互斥
linux线程基础----线程同步与互斥 一.同步的概念 1.同步概念 所谓同步,即同时起步,协调一致.不同的对象,对"同步"的理解方式略有不同.如,设备同步,是指在两个设备 ...
最新文章
- matlab 基于 libsvm工具箱的svm分类遇到的问题与解决
- 用脑机接口去“搜索一下”,是种什么体验? | CCF C³-03@搜狗
- Shell 与Python的交互
- Redis张工的set存储结构(实现)原理
- 通过@Value + @PropertySource来给组件赋值
- C#/Net代码精简优化技巧(1)
- 纳尼???我JVM优化过头了,直接把异常信息优化没了?怎么办
- html5 密码框明文,elementUI的密码框的密文和明文
- Html5不可见标签,及标签属性(元素对象属性) a href target name id 相对路径
- jStat:轻量级Javascript统计库
- 用计算机运算符编写检索式,检索式
- ab plc软件_回收拆厂拆机设备废旧设备PLC触摸屏自动化设备回收【cpu吧】
- 如何用手机远程协助长辈?我找出了6个最佳方法!(免ROOT)
- mysql secure_file_priv 属性相关的文件读写权限问题
- 图片按指定比例缩放并压缩至指定大小,解决保存图片文件体积过大bug。
- 以极地号为例认识科考船上的各种设备
- 媒体-PR-微商-地摊儿…… 媒体人的转型你到了哪一步?
- EOS智能合约开发系列(十): 抵御彩虹攻击
- NEXTCHIP-图像优化师
- 接口测试神器,它来了,它带着光环走来了