1 自旋锁的使用
linux系统中与自旋锁相关的操作主要有如下四种:
(1)定义自旋锁
spinlock_t spin;
(2)初始化自旋锁
spin_lock_init(lock)
//该宏用于动态初始化自旋锁lock
(3)获取自旋锁
spin_lock(lock)//该宏用于获取自旋锁lock,如果立马获得锁,就立刻返回,
//否则将自锁在那里,直到该自旋锁的持有者释放。
spin_trylock(lock)//该宏用于尝试获得自旋锁。
(4)释放自旋锁
spin_unlock(lock)//该宏用于释放自旋锁,与spin_lock及spin_trylock配对使用
自旋锁一般这样使用:
//定义自旋锁
spinlock_t lock;
spin_lock_init(&lock);
spin_lock(&lock);//获取自旋锁保护临界区
...//临界区
spin_unlock(&lock);//解锁
说明:
(1)自旋锁主要应用于smp或单cpu但内存可抢占的情况。
(2)自旋锁得到的代码路径在执行临界区的时候可能受到中断和底半部的影响,可以通过自旋锁的下列衍生解决
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()
(3)自旋锁是忙等锁,只有在占用锁时间极短的情况下使用才是合理的
(4)自旋锁可能导致系统崩溃,使用copy_to_user(),copy_from_user()和kmalloc()函数可能引起阻塞,因此在自旋锁的占用期间不能调用这些函数。
代码清单7.2 使用自旋锁使设备只能被一个进程打开
- int xxx_count = 0;
- static int xxx_open(struct inode *inode,struct file *filp)
- {
- ...
- spinlock(&xxx_lock);
- if(xxx_count)
- {
- spin_unlock(&xxx_lock);
- return - EBUSY;
- }
- xxx_count++;
- spin_unlock(&xxx_lock);
- ...
- return 0;
- }
- static int xxx_release(struct inode *inode,struct file *filp)
- {
- ...
- spinlock(&xxx_lock);
- xxx_count--;
-
- spin_unlock(&xxx_lock);
- return 0;
-
- }
2 读写自旋锁
读写自旋锁是一种比自旋锁力度更小的锁机制,他保留了"自锁"的概念,但是在写方面,只能最多有一个写进程,在读方面,同时可以有多个读执 行单元,当然读写不能同时进行。
读写自旋锁的操作如下:
1)定义和初始化读写自旋锁
rwlock_t my_rwlock = RW_LOCK_UNLOCKED;//静态初始化
rwlock_t my_rwlock;
rwlock_init(&my_rwlock);//动态初始化
2)读锁定
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);
3)读解锁
void read_unlock(rwlock_t *lock);
void read_unlock_irqsave(rwlock_t *lock,unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
说明:在对共享资源进行读取之前,应该先调用读锁定函数,完成之后应调用解读锁定函数
4)写锁定
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);
void write_trylock(rwlock_t *lock);
5)写解锁
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);
说明:在对共享资源进行读取之前,应该先调用写锁定函数,完成之后应调用解写锁定函数
读写自旋锁的使用方法如下:
rwlock_t lock;//定义读写自旋锁
rwlock_init(&lock);//初始化
//读时获取锁
read_lock(&lock);
...//临界资源
read_unlock(&lock);
//写时获取锁
write_lock_irqsave(&lock,flags);
...//临界资源
write_unlock_irqrestore(&lock,flags);
3 顺序锁
顺序锁是对读写锁的一种优化,使用顺序锁,读执行单元不会被写执行单元堵塞。
linux内核中,写执行单元涉及如下顺序锁操作
1)获得顺序锁
void write_seqlock(seqlock_t *s1);
int write_tryseqlock(seqlock_t *s1);
write_seqlock_irqsave(lock,flags);
write_seqlock_irqe(lock);
write_seqlock_bh(lock);
2)释放顺序锁
void write_sequnlock(seqlock_t *s1);
write_sequnlock_irqrestore(lock,flags);
write_sequnlock_irqe(lock);
write_sequnlock_bh(lock);
//顺序锁的使用模式:
write_seqlock(&seqlock_a);
...//写操作代码块
write_sequnlock(&seqlock_a);
读执行单元涉及如下操作:
1)读开始
unsigned read_seqbegin(const seqlock_t *s1);
read_seqbegin_irqsave(lock,flags)
2)重读
int read_seqretry(const seqlock_t *s1,unsigned iv);
read_seqretry_irqrestore(lock,iv,flags);
读执行单元访问顺序锁的模式如下:
do{
seqnum = read_seqbegin(&seqlock_a)
//读操作代码块
...
}while(read_seqretry(&seqlock_a,seqnum));
4 读-拷贝-更新(RCU)
RCU可以看做读写锁的高性能版本,相比读写锁,RCU的优点在于既允许多个读执行单元同时访问被保护的数据,又允许多个读执行单元和多个写执行单元同时访问被保护的数据。
但是,RCU不能代替读写锁,因为如果写比较多时,对读执行单元的性能提高不能弥补写执行单元导致的损失。因为使用RCU时,写执行单元之间的同步开销会比较大,它需要延迟数据结构的释放,复制被修改的数据结构,它也必须使用某种锁机制同步并行的其他写执行单元的修改操作。
linux系统提供如下4中RCU的操作
1)读锁定
rcu_read_lock()
rcu_read_lock_bh()
2)读解锁
rcu_read_unlock()
rcu_read_unlock_bh()
使用模式如下:
rcu_read_lock()
...//读临界区
rcu_read_unlock()
实际上,rcu_read_lock(),rcu_read_unlock()实质上只是禁止和使能内核的抢占进度
3)同步RCU
synchronize_rcu()
该函数由RCU写执行单元调用,将阻塞写执行单元,直到所有读执行单元已经完成度执行单元临界区 ,写执行单元才可以继续下一步。
4)挂接回调
void fastcall call_rcu(struct rcu_head *head,void (*func)(struct rcu_head *rcu));
该函数也由RCU的写执行单元调用,他不会使写执行单元阻塞,因而可以在中断上下文或软中断中使用。该函数 将由函数func挂接到RCU回调函数链上,然后立即返回。