并发与竞态
(1)、中断屏蔽
概念:在单CPU范围内避免竞态的一种简单而省事的方法是在进入临界区这前屏蔽系统的中断,此段时间内不再响应中断。可以长时间屏蔽中断是很危险的。
用法:一般用在对硬件寄存器上操作
local_irp_disable()
…..
critical section
…...
local_irq_enable()
优点:
缺点:1、不可长时间屏蔽,中断后要应当尽快地执行完临界区的代码。
2、只能禁止和使能本CPU内的中断,并不能解决SMP多CPU引发的竞态。
函数列表:
保存中断信息
local_irq_save(flag)
local_irq_restore(flag)
禁止和使能底半部中断
local_bh_disable()
local_bh_enable()
(2)、原子操作
概念:原子操作是指在执行过程中不会被别的代码路径所中断的操作。
整型原子操作:
1、 设置原子变量的值
void atomic_set(atomic_t *v, int i)
atomic_t v = ATOMIC_INIT(0);
2、 获取原子的变量值
atomic_read(atomic_t *v)
3、 原子变量加减
void atomic_add(int i, atomic_t *v)
void atomic_sub(int i, atomic_t *v)
4、 原子变量自增/自减
void atomic_inc(atomic_t *v)
void atomic_dec(atomic_t *v)
5、 操作并测试(并没有加减)
int atomic_inc_and_test(atomic_t *v)
int atomic_dec_and_test(atomic_t *v)
int atomic_sub_and_test(atomic_t *v)
6、 操作并返回
int atomic_add_return(int i, atomic *v)
优点:
缺点:
(3)、自旋锁
概念:在SMP体系下引入,操作一个测试并设置的原子,程序将在一个小的循环重复这个“测试并设置”操作,即进行所谓的“自旋”,通俗地说就是“在原地打转”
使用方法:
spinlock_t lock;
spinlock_lock_init(&lock);
spin_lock(&lock)
…..
spin_unlock(&lock)
优点:1、主要针对SMP或单CPU但内核可抢占的情况。
注意事项:
1、 自旋锁是忙等待锁。临界区要小
2、 自旋锁可能导致系统死锁。一个CPU已获得自旋锁,但还想再次获得这个锁。
3、 自旋锁定期间不能调用可能引起进程调度的函数。如调用:copy_from_user()、copy_to_user()、kmalloc()和msleep等函数。
自旋锁的衍生:
spin_lock_irqsave()
spin_lock_irqrestore()
spin_lock_bh()
spin_unlock_bh()
(4)、读写自旋锁
http://www.ibm.com/developerworks/cn/linux/l-cn-rwspinlock1/
概念:读/写自旋锁同样是在保护SMP体系下的共享数据结构而引入的,它的引入是为了增加内核的并发能力。只要内核控制路径没有对数据结构进行修改,读/写自旋锁就允许多个内核控制路径同时读同一数据结构。如果一个内核控制路径想对这个结构进行写操作,那么它必须首先获取读/写锁的写锁,写锁授权独占访问这个资源。这样设计的目的,即允许对数据结构并发读可以提高系统性能。即:只读时,它允许多个进程来读,当要写时,它允许当前只有一个在写,写完后才允许读
使用方法:
rwlock_t rwlock;
rwlock_init(&rwlock);
读锁定
void read_lock(rwlock_t *lock)
void read_lock_irqsave(rwlock_t *lock, unlong flags);
void read_lock_iqr(rwlock_t *lock)
void read_lock_bh (rwlock_t *lock)
写锁定
void write_lock(rwlock_t *lock)
void write_lock_irqsave(rwlock_t *lock, unlong flags);
void write_lock_iqr(rwlock_t *lock)
void write_lock_bh (rwlock_t *lock)
(5)、顺序锁
概念:读写不必阻塞,但是写与写会被阻塞。如果读执行单元在读操作期间,写执行单元已经发生了写操作,那么,读执行单元必须重新读取数据。
(6)、信号量
概念:和自旋锁一样,但进程不会原地打转而是进入休眠等待状态。
特点:可引起进程休眠,不能在中断上下文使用
使用方法:
struct semaphore sem;
sema_init(Struct semaphore *sem, int val);
down(&sem);
….
up(&sem);
(7)、读写信号量
概念:读写信号量可能引起进程阻塞,但它可以允许多个读执行单元同时访问共享资源,而最多只能有1个写执行单元。
使用方法:
rw_semaphore rw_sem;
init_rwsem(&rw_sem);
读:
down_read(&rw_sem);
….
up_read(&rw_sem);
写:
down_write(&rw_sem);
….
up_write(&rw_sem);
(8)、互斥体
使用方法:
struct mutex mutex;
mutex_init(&mutex);
mutex_lock(&mutex);
….临界资源
mutex_unlock(&mutex);