并发与竞态 (自旋锁)
简介
自旋锁: 它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。----百度百科
由以上介绍大概了解,自旋锁就是内核为防止临界区被多个进程同时访问出错的一种机制。其作用是保证临界区在任一时刻有且仅有一个进程访问。
应用场景
举个例子: app1: 向驱动设备(/dev/driver_case)写入1024个字节的'a'字符,然后读取,25s后再次读取; app2: 向驱动设备(/dev/driver_case)写入1024个字节的'b'字符,然后读取,25s后再次读取; 驱动设备:注册驱动/dev/driver_case。将app写入的数据存入到内核空间,并在app读取时打印出来。
问题:两个app都会占用驱动25s,如果先执行app1占用驱动,在app1没有结束占用之前,再执行app_2占用了驱动,会出现什么后果? 现象:
第一次访问.png
第二次访问.png
分析:第一次读取时,发现app1写入的是‘a’,app2写入的是‘b’。但是第二次读取时,发现app1写入的值被app2覆盖掉了,这就会出现问题了。
解决方法:驱动引入自旋锁,在驱动被一个进程占用没有释放之前,不允许其他进程占用。
实例分析
1.声明自旋锁结构体变量
#include <linux/uaccess.h>
……
struct driver_case_dev {
……
#ifdef SPINLOCK_SUPPORT
spinlock_t lock;
#endif
};
struct driver_case_dev driver_case;
2.初始化自旋锁
……
spin_lock_init(&driver_case.lock);
……
3.入口设计
static int driver_case_open(struct inode *inode, struct file *filp)
{
#ifdef SPINLOCK_SUPPORT
unsigned long flags;
spin_lock_irqsave(&driver_case.lock, flags);
if (driver_case.dev_status) {
spin_unlock_irqrestore(&driver_case.lock, flags);
return -EBUSY;
}
driver_case.dev_status++;
spin_unlock_irqrestore(&driver_case.lock, flags);
#endif
……
return 0;
}
4.出口设计
static int driver_case_release(struct inode *inode, struct file *filp)
{
#ifdef SPINLOCK_SUPPORT
unsigned long flags;
spin_lock_irqsave(&driver_case.lock, flags);
if (driver_case.dev_status) {
driver_case.dev_status--;
}
spin_unlock_irqrestore(&driver_case.lock, flags);
#endif
……
return 0;
}
注意:需要注意的是,这里自旋锁保护的临界区是driver_case.dev_status增减操作。驱动通过判断driver_case.dev_status的值来确定当前驱动是否被占用。 这里的例子效果不是很明显,理想的是在open入口直接给上一个自旋锁,然后在release出口函数释放。但是自旋锁的缺点是在整个持锁过程中,会导致访问的进程阻塞,且本例期间会屏蔽中断。由于app占用时间25s,会导致期间中断不会响应,且进程长时间阻塞。因此没有按照理想情况设计,driver_case.dev_status增减操作执行很快,不会导致以上问题出现,因此选用此种方案。
测试
测试图.png
由上可知,在执行第一个app时,是正常运行的。此时再执行第二个app时,会发现无法访问驱动。从而避免驱动在被占用期间,再次被其他进程访问的问题了。
- 关于分区表的在线重定义(r5笔记第98天)
- 10个实用的但偏执的Java编程技术
- 看似诡异的tablespace online问题(r5笔记第95天)
- python2.7进行爬虫POI代码(划分小网格算法)
- 从 Encoder 到 Decoder 实现 Seq2Seq 模型
- python2.7进行爬虫百度POI代码(划分小网格算法)
- 如何通过TTL调试光猫
- 基于树莓派和Tensowflow的物体识别-brain
- SNA中:中心度及中心势诠释(不完整代码)
- 教程 | 基于计算机视觉使用Python和OpenCV计算道路交通
- 干货 | MVP模式在携程酒店的应用和扩展
- memlock过低导致的数据库性能问题(r6笔记第10天)
- OpenCV和SVM分类器在自动驾驶中的车辆检测
- 自动驾驶的模型预测控制
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- BFE.dev前端刷题#13. 利用栈(Stack)创建队列(Queue)
- WebAssembly之wasm格式解析
- 消息队列
- 在java中boolean类型占多少字节?
- 调素数
- DFS(小白式超详细讲解以及代码讲解)
- 判断一个序列是否有序(升序或者降序)
- 有关dp问题的机器人走地图
- 有关结构体内重载操作的最详细的讲解~~
- 洛谷 P1002 DP解法
- AtCoder Beginner Contest 163 A~~D 详细代码讲解
- HUD 4841 (vector的用法)
- c++ 优先队列(priority_queue)的详细讲解用法
- HDU 1022(关于栈的详细解法)
- count_if函数的用法