深入浅出:Linux设备驱动中的并发控制
作者:网络转载 发布时间:[ 2015/5/7 13:34:54 ] 推荐标签:操作系统
下面来举一个实例,是原子变量使用实例,使设备只能被一个进程打开:
static atomic_t xxx_available = ATOMIC_INIT(1); // 定义原子变量
static int xxx_open(struct inode *inode, struct file *filp)
{
...
if(!atomic_dec_and_test(&xxx_available))
{
atomic_inc(&xxx_availble);
return - EBUSY; // 已经打开
}
...
return 0; // 成功
}
static int xxx_release(struct inode *inode, struct file *filp)
{
atomic_inc(&xxx_available); // 释放设备
return 0;
}
我要着重谈一下:
自旋锁VS信号量
从严格意义上来说,信号量和自旋锁属于不同层次的互斥手段,前者的实现依赖于后者,在多CPU中需要自旋锁来互斥。信号量是进程级的,用于多个进程之间对资源的互斥,虽然也在内核中,但是该内核执行路径是以进程的身份,代表进程来争夺资源的。如果竞争失败,会切换到下个进程,而当前进程进入睡眠状态,因此,当进程占用资源时间较长时,用信号量是较好的选择。
当所要保护的临界访问时间比较短时,用自旋锁是非常方便的,因为它节省了上下文切换的时间。但是CPU得不到自旋锁是,CPU会原地打转,直到其他执行单元解锁为止,所以要求锁不能在临界区里停留时间过长。
自旋锁的操作步骤:
1.定义自旋锁
spinlock_t lock;
2.初始化自旋锁
spin_lock_init(lock);这是个宏,它用于动态初始化自旋锁lock;
3.获得自旋锁
spin_lock(lock);该宏用于加锁,如果能够立即获得锁,它能马上返回,否则,他将自旋在那里,直到该自旋锁的保持者释放。
spin_trylock(lock);能够获得,则返回真,否则返回假,实际上是不在原地打转而已。
4.释放自旋锁
spin_unlock(lock);
自旋锁持有期间内核的抢占将被禁止。 自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰,但是得到锁的代码路径在执行临界区的时候还可能受到中断和底半部(BH)的影响。为防止这种影响,需要用到自旋锁的衍生:
spin_lock_irq() = spin_lock() + local_irq_disable()
spin_unlock_irq() = spin_unlock() + local_irq_enable()
spin_lock_irqsave() = spin_lock() + local_irq_save()
spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()
spin_lock_bh() = spin_lock() + local_bh_disable()
spin_unlock_bh() = spin_unlock() + local_bh_enable()
注意:自旋锁实际上是忙等待,只有在占用锁的时间极短的情况下,使用自旋锁才是合理的自旋锁可能导致死锁:递归使用一个自旋锁或进程获得自旋锁后阻塞。
例子:
spinlock_t lock;
spin_lock_init(&lock);
spin_lock(&lock); //获取自旋锁,保护临界区
。。。。临界区
spin_unlock(&lock);//释放自旋锁
自旋锁不关心锁定的临界区究竟是如何执行的。不管是读操作还是写操作,实际上,对共享资源进行读取的时候是应该可以允许多个执行单元同时访问的,那么这样的话,自旋锁有了弊端。于是便衍生出来一个读写锁。它保留了自旋的特性,但在对操作上面可以允许有多个单元进程同时操作。当然,读和写的时候不能同时进行。
现在又有问题了,如果我第一个进程写共享资源,第二个进程读的话,一旦写了,那么读不到了,可能写的东西比较多,但是第二个进程读很小,那么能不能第一个进程写的同时,我第二个进程读呢?
当然可以,那么引出了顺序锁的概念。都是一样的操作。
读写自旋锁(rwlock)允许读的并发。在写操作方面,只能多有一个写进程,在读操作方面,同时可以有多个读执行单元。当然,读和写也不能同时进行。
// 定义和初始化读写自旋锁
rwlock_t my_rwlock = RW_LOCK_UNLOCKED; // 静态初始化
rwlock_t my_rwlock;
rwlock)init(&my_rwlock); // 动态初始化
// 读锁定:在对共享资源进行读取之前,应先调用读锁定函数,完成之后调用读解锁函数
void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);
void read_lock_bh(rwlock_t *lock);
// 读解锁
void read_unlock(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
// 写锁定:在对共享资源进行写之前,应先调用写锁定函数,完成之后调用写解锁函数
void write_lock(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_lock_irq(rwlock_t *lock);
void write_lock_bh(rwlock_t *lock);
int write_trylock(rwlock_t *lock);
// 写解锁
void write_unlock(rwlock_t *lock);
void write_unlock_irqsave(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
Linux下开源的DDR压力测试工具曝Linux恶意软件:让树莓派设备挖掘数字货币linux系统中不同颜色的文件夹及根目录介绍软件测试工程师必知必会Linux命令Linux下DNS服务器配置如何成为不可替代的Linux运维工程师?详解Linux进程(作业)的查看和杀死Linux 日志定时轮询流程详解比特币勒索病毒不只Windows系统有,Linux版的来了Linux日志定时轮询流程详解Linux iommu和vfio概念空间解构Linux系统如何低于TCP洪水攻击Linux无损调整分区大小Linux下防火墙配置实例Linux使用Jexus托管Asp.Net Core应用程序Linux中引号的那些事
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南