一、Linux驱动下的并发问题
问题描述:
如:编写好了一个驱动程序,单在同一时间有两个进程用这个驱动程序都open同一个文件时那么就会涉及"资源冲突"的问题
解决方案:
我们只要确保在open文件时有一个flag判断,如果已经打开过那么判断flag应该不能够在次进去
二、具体方法:
1.原子操作
-
定义一个原子变量
atomic_t lock;
-
在xxx_init中初始化
atomic_set(&lock, 1);
-
在xxx_open中
int xxx_open (struct inode *inode, struct file *file)//不允有两个同时打开 { printk("%s %s %d\n", __FILE__,__FUNCTION__,__LINE__); if (!atomic_dec_and_test(&lock)) { atomic_inc(&lock);/* 小于 0 的话就加 1,使其原子变量等于 0 */ return -EBUSY; /* LED 被使用,返回忙 */ } return 0; }
-
在xxx_release中
//释放设备是恢复原子变量 atomic_set(&lock, 1);
2.互斥锁(互斥锁是信号量的一种特殊形式,而信号量又是基于自旋锁的)
-
在全局下定义一个互斥体
struct mutex lock;
-
在xxx_init中初始化
mutex_init(&lock);
-
在xxx_open中
int xxx_open (struct inode *inode, struct file *file)//不允有两个同时打开 { unsigned long flags; printk("%s %s %d\n", __FILE__,__FUNCTION__,__LINE__); /* 获取互斥体,可以被信号打断 */ if (mutex_lock_interruptible(&lock)) { return -ERESTARTSYS; } #if 0 mutex_lock(lock); /* 不能被信号打断 */ #endif return 0; }
-
在xxx_release中
/* 释放互斥锁 */ mutex_unlock(&lock);
3.自旋锁(它是一种在多处理器下共享的变量(才能实现进程间的互斥))
-
在全局下定义一个自旋锁与互斥变量(普通的flag)
static int lock_status;//互斥变量,如果这个变量为1表示没占用,0表示占用 spinlock_t lock;//自旋锁
-
在xxx_init中初始化
不需要初始化
-
在xxx_open中
int xxx_open (struct inode *inode, struct file *file)//不允有两个同时打开 { unsigned long flags; printk("%s %s %d\n", __FILE__,__FUNCTION__,__LINE__); spin_lock_irqsave(&lock, flags); if(lock_status==1){ lock_status--; spin_unlock_irqrestore(&lock, flags); } else{ spin_unlock_irqrestore(&lock, flags); return -EBUSY; } return 0; }
-
在xxx_release中
int xxx_release (struct inode *inode, struct file *file) { printk("%s %s %d\n", __FILE__,__FUNCTION__,__LINE__); //恢复互斥变量 spin_lock_irqsave(&lock, flags); lock_status++; spin_unlock_irqrestore(&lock, flags); return 0; }
-
<(81条消息) spin_lock、spin_lock_irq、spin_lock_irqsave区别_luckywang1103的博客-CSDN博客_spinlock_irq>
3.5读写锁(rwlock)
读写锁存在的意义就是需要在经常读临界资源的情况下能够比普通自旋锁更高效
规则:
读时能读,写时不能读/写,读时不能写。
1.定义读写锁
rwlock_t open_lock;
2.读锁
read_lock_irqsave(&open_lock, flag);
read_unlock_irqrestore(&open_lock, flag);
3.写锁
write_lock_irqsave(&open_lock, flag);
write_unlock_irqrestore(&open_lock, flag);
4.信号量
-
在全局下定义一个信号量
struct semaphore sem;
-
在xxx_init中初始化
sema_init(&sem, 1);
-
在xxx_open中
int xxx_open (struct inode *inode, struct file *file)//不允有两个同时打开 { unsigned long flags; printk("%s %s %d\n", __FILE__,__FUNCTION__,__LINE__); if (down_interruptible(&sem)) {//运行中断打断的信号量获取 return -ERESTARTSYS;//如果不是以正常方式(取得信号量)返回,则报错 } #if 0 down(&sem); #endif return 0; }
-
在xxx_release中
int led_release (struct inode *inode, struct file *file) { printk("%s %s %d\n", __FILE__,__FUNCTION__,__LINE__); up(&sem);//释放信号量 return 0; }
三、四种方式的区别与选择
-
原子操作:不能被中断打断,最简单的原子操作就是一条条的汇编指令(不包括一些伪指令,伪指令会被汇编器解释成多条汇编指令)
适用于保护一个具体的临界变量。
-
互斥体:信号量是在并行处理环境中对多个处理器访问某个公共资源进行保护的机制,mutex用于互斥操作。
注意事项:
- 不能嵌套
- 不能在中断中加锁(可睡眠)
-
信号量:它是用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,用来记录对某个资源(如共享内存)的存取状况。
-
自旋锁: Spinlock 是内核中提供的一种比较常见的锁机制,自旋锁是“原地等待”的方式解决资源冲突的,即,一个线
程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到,只能够原地“打转”(忙等待)。
注意:
- 一般用于多cpu(如果用单cpu那么等待时唯一的一个cpu用来忙等待,就什么也不能干了)
- 可以用于中断(指的是在中断中获取锁),但是在中断中不能获取同一个自旋锁,否则死锁
5.