1、Linux信号量实现:
(1)直接创建信号量:
void sema_init(struct semaphore *sem, int val);
其中val是赋予信号量一个初始的值。
(2)用下面的方法声明和初始化一个互斥体:
DECLARE_MUTEX(name);
//一个称为name的信号量被初始化为1
DECLARE_MUTEX_LOCKED(name);
//一个称为name的信号量被初始化为0
(3)如果互斥体必须在运行时被初始化,则使用下面的方法:
void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
(4)P操作:
void down(struct semaphore *sem);
//必要时一直等待
int down_interruptable(struct semaphore *sem);
//可中断
int down_trylock(struct semaphore *sem);
//永远不会休眠
(5)V操作:
void up(struct semaphore *sem);
2、读者/写者信号量:
(1)必须包含头文件:<linux/rwsem.h>
(2)一个rwsem信号量在运行时必须用下面的函数显示的初始化:
void init_rwsem(struct rw_semaphore *sem);
(3)只读访问:
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);
(4)写入者接口:
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
void downgrade_write(struct rw_semaphore *sem);
3、自旋锁API介绍:
(1)自旋锁编译阶段的初始化:
spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
(2)在运行时调用下面的函数初始化:
void spin_lock_init(spinlock_t *lock);
(3)进入临界区之前我们的代码必须调用下面的函数获取需要的锁:
void spin_lock(spinlock_t *lock);
(4)释放已经获取的锁:
void spin_unlock(spinlock_t *lock);
4、
自旋锁的代码都必须是原子的,自旋锁必须在最短的时间内拥有(否则可能会造成优先级翻转)
5、自旋锁函数:
(1)锁定一个自旋锁的函数:
void spin_lock(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags); //获取自旋锁之前禁止中断,而先前的中断状态保存在flags中。
void spin_lock_irq(spinlock_t *lock);//如果没有其他任何代码禁止本地中断,则可使用,无需flags
void spin_lock_bh(spinlock *lock);//禁止软终端,已中断保持打开
(2)释放自旋锁:
void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
6、读者/写者自旋锁
(1)定义在<linux/spinlock.h>
rwlock_t my_rwlock = RW_LOCK_UNLOCKED; /*Static way*/
rwlock_t my_lock;
rwlock_init(&my_lock); /*Dynamic way*/
(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);
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);
(3)写者自旋锁可用操作:
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_irqrestore(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
7、锁陷阱:
获取局部所和内核锁时,先获得局部锁再获得内核锁;信号量和自旋锁需要先获得信号量再去申请自旋锁,不允许自旋锁去试图获得信号量,因为down(可能导致休眠)。
如果我们的确怀疑锁竞争导致性能下降,则可以使用lockmeter工具。
8、原子变量:
定义在<asm/atomic.h>
类型:atomic_t,在所有内核支持的架构上保存一个int值,但是只能记录不大于24位的整数。
(1)将原子变量V的值设置成i:
void atomic_set (stomic_t *V, int i);
或
atomic_t V = ATOMIC_INT(0);//编译时初始化V的值。
(2)返回v的值:
int atomic_read(atomic_t *v);
(3)将i累加到v指向的原子变量:
void atomic_add(int i, atomic_t *v);
(4)从*v中减去i:
void atomic_sub(int i, atomic_t *v);
(5)增加或减去一个原子变量:
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);
(6)执行特定的操作并测试结果;如果在操作结束后,原子值为0,则返回值为true;否则返回为false。注意,不存在atomic_add_and_test函数。
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
(7)将整数值i累加到v。返回值结果为负时为true,否则为false。
int atomic_add_negative(int i, atomic_t *v);
(8)类似于atomic_add及其变种,例外之处在于这些函数会将新的值返回给调用者。
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
注:要是一个原子操作,则无需加锁,若某个操作中有多个原子操作则需要加锁,因为原子操作之间会隔一段时间。
7、位操作
8、seqlock
(1)需要包含头文件:<linux/seqlock.h>
初始化seqlock的两种方法:
seqlock_t lock1 = SEQLOCK_UNLOCKED;
seqlock_t lock2;
seqlock_init(&lock2);