目录
一、自旋锁:
自旋锁(spin lock)是一种对临界资源进行互斥手访问的典型手段,其名称来源于它的工作方式。
为了获得一个自旋锁,在某CPU上运行的代码需先执行一个原子操作,该操作测试并设置(test-and-set)某个内存变量,由于它是原子操作,所以在该操作完成之前其他执行单元不可能访问这个内存变量。
由于自旋锁适用于保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。
DEFINE_SPINLOCK(x)
静态初始化自旋锁
#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
举例:
static DEFINE_SPINLOCK(mdp_lock);
static DEFINE_SPINLOCK(mdss_mdp_intr_lock);
spin_lock_init(x)
动态初始化自旋锁
举例:
spinlock_t mdss_mdp_intr_lock;
spin_lock_init(&mdss_mdp_intr_lock);
int spin_trylock(spinlock_t *lock)
该宏尽力获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则不能立即获得锁,立即返回假。它不会自旋等待lock被释放。
举例:
static DEFINE_SPINLOCK(enable_lock);
unsigned long flags = 0;
int is_locked = 0;
is_locked = spin_trylock_irqsave(&enable_lock, flags);
if (is_locked)
spin_unlock_irqrestore(&enable_lock, flags);
void spin_lock(spinlock_t *lock)
用于获得自旋锁lock,如果能够立即获得锁,它就马上返回,否则,它将自旋在那里,直到该自旋锁的保持者释放,这时,它获得锁并返回。总之,只有它获得锁才返回。
void spin_unlock(spinlock_t *lock)
释放自旋锁lock,它与spin_trylock或spin_lock配对使用。如果spin_trylock返回假,表明没有获得自旋锁,因此不必使用spin_unlock释放。
举例:
spin_lock(&mdss_mdp_intr_lock);
spin_unlock(&mdss_mdp_intr_lock);
spin_lock_irqsave(lock, flags)
该宏获得自旋锁的同时把标志寄存器的值保存到变量flags中并失效本地中断。
#define spin_lock_irqsave(lock, flags) \
do { \
raw_spin_lock_irqsave(spinlock_check(lock), flags); \
} while (0)
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
释放自旋锁lock的同时,也恢复标志寄存器的值为变量flags保存的值。它与spin_lock_irqsave配对使用。
举例:
unsigned long flags;
spin_lock_irqsave(&mdss_mdp_intr_lock, flags);
spin_unlock_irqrestore(&mdss_mdp_intr_lock, flags);
void spin_lock_irq(spinlock_t *lock)
类似于spin_lock_irqsave,只是该宏不保存标志寄存器的值。 //不推荐使用,除非能明确中断在加锁前是激活的
void spin_unlock_irq(spinlock_t *lock)
释放自旋锁lock的同时,也使能本地中断。它与spin_lock_irq配对应用。 //不推荐使用,无条件在解锁时激活中断
举例:
spin_lock_irq(&mdss_mdp_intr_lock); //不推荐使用,除非能明确中断在加锁前是激活的,
spin_unlock_irq(&mdss_mdp_intr_lock); //不推荐使用,无条件在解锁时激活中断
void spin_lock_bh(spinlock_t *lock)
得到自旋锁的同时失效本地软中断。
void spin_unlock_bh(spinlock_t *lock)
释放自旋锁lock的同时,也使能本地的软中断。它与spin_lock_bh配对使用。
spin_trylock_irqsave(lock, flags)
该宏如果获得自旋锁lock,它也将保存标志寄存器的值到变量flags中,并且失效本地中断,如果没有获得锁,它什么也不做。
因此如果能够立即获得锁,它等同于spin_lock_irqsave,如果不能获得锁,它等同于spin_trylock。如果该宏获得自旋锁lock,那需要使用spin_unlock_irqrestore来释放。
#define spin_trylock_irqsave(lock, flags) \
({ \
raw_spin_trylock_irqsave(spinlock_check(lock), flags); \
})
int spin_trylock_irq(spinlock_t *lock)
似于spin_trylock_irqsave,只是不保存标志寄存器。如果获得自旋锁lock,需要使用spin_unlock_irq来释放。
int spin_trylock_bh(spinlock_t *lock)
如果获得了自旋锁,它也将失效本地软中断。如果得不到锁,它什么也不做。因此,如果得到了锁,它等同于spin_lock_bh,如果得不到锁,它等同于spin_trylock。
如果该宏得到了自旋锁,需要使用spin_unlock_bh来释放。
int spin_is_locked(spinlock_t *lock)
用于判断自旋锁x是否已经被某执行单元保持(即被锁),如果是,返回真,否则返回假。
int spin_can_lock(spinlock_t *lock)
用于判断自旋锁lock是否能够被锁,它实际是spin_is_locked取反。如果lock没有被锁,它返回真,否则,返回假。
void spin_unlock_wait(spinlock_t *lock)
用于等待自旋锁x变得没有被任何执行单元保持,如果没有任何执行单元保持该自旋锁,该宏立即返回,否则将循环在那里,直到该自旋锁被保持者释放。
获得自旋锁和释放自旋锁有好几个版本,因此让读者知道在什么样的情况下使用什么版本的获得和释放锁的宏是非常必要的。
如果被保护的共享资源只在进程上下文访问和软中断上下文访问,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用spin_lock_bh和spin_unlock_bh来保护。
当然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用spin_lock_bh和spin_unlock_bh是最恰当的,它比其他两个快。
如果被保护的共享资源只在进程上下文和tasklet或timer上下文访问,那么应该使用与上面情况相同的获得和释放锁的宏,因为tasklet和timer是用软中断实现的。
如果被保护的共享资源只在一个tasklet或timer上下文访问,那么不需要任何自旋锁保护,因为同一个tasklet或timer只能在一个CPU上运行,即使是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。
timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。当然同一个tasklet有两个实例同时运行在同一个CPU就更不可能了。
如果被保护的共享资源只在两个或多个tasklet或timer上下文访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。
如果被保护的共享资源只在一个软中断(tasklet和timer除外)上下文访问,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。
如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。
如果被保护的共享资源在软中断(包括tasklet和timer)或进程上下文和硬中断上下文访问,那么在软中断或进程上下文访问期间,可能被硬中断打断,从而进入硬中断上下文对共享资源进行访问,因此,在进程或软中断上下文需要使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
而在中断处理句柄中使用什么版本,需依情况而定,如果只有一个中断处理句柄访问该共享资源,那么在中断处理句柄中仅需要spin_lock和spin_unlock来保护对共享资源的访问就可以了。
因为在执行中断处理句柄期间,不可能被同一CPU上的软中断或进程打断。但是如果有不同的中断处理句柄访问该共享资源,那么需要在中断处理句柄中使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
在使用spin_lock_irq和spin_unlock_irq的情况下,完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代,那具体应该使用哪一个也需要依情况而定,如果可以确信在对共享资源访问前中断是使能的,那么使用spin_lock_irq更好一些。
因为它比spin_lock_irqsave要快一些,但是如果你不能确定是否中断使能,那么使用spin_lock_irqsave和spin_unlock_irqrestore更好,因为它将恢复访问共享资源前的中断标志而不是直接使能中断。
当然,有些情况下需要在访问共享资源时必须中断失效,而访问完后必须中断使能,这样的情形使用spin_lock_irq和spin_unlock_irq最好。
spin_lock用于阻止在不同CPU上的执行单元对共享资源的同时访问以及不同进程上下文互相抢占导致的对共享资源的非同步访问,而中断失效和软中断失效却是为了阻止在同一CPU上软中断或中断对共享资源的非同步访问。
二、读写自旋锁
读写自旋锁(rwlock)是一种比自旋锁粒度更小的自旋锁机制,它保留了“自旋”的概念。
但是在写操作方面,只能最多有一个写进程,在读方面,同时可拥有多个执行单元,当然读和写也不能同时进行。
一句话:锁写不锁读
DEFINE_RWLOCK(x) //宏
静态初始化
#define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x)
举例:
static DEFINE_RWLOCK(bp_lock);
rwlock_init(lock) //宏
动态初始化
举例:
rwlock_t x;
rwlock_init(&x); //动态初始化
#define read_trylock(lock) __cond_lock(lock, _raw_read_trylock(lock))
#define write_trylock(lock) __cond_lock(lock, _raw_write_trylock(lock))
#define write_lock(lock) _raw_write_lock(lock)
#define read_lock(lock) _raw_read_lock(lock)
#define read_lock_irq(lock) _raw_read_lock_irq(lock)
#define read_lock_bh(lock) _raw_read_lock_bh(lock)
#define write_lock_irq(lock) _raw_write_lock_irq(lock)
#define write_lock_bh(lock) _raw_write_lock_bh(lock)
#define read_unlock(lock) _raw_read_unlock(lock)
#define write_unlock(lock) _raw_write_unlock(lock)
#define read_unlock_irq(lock) _raw_read_unlock_irq(lock)
#define write_unlock_irq(lock) _raw_write_unlock_irq(lock)
read_lock(lock); //如果不能获得,它将自旋,直到获得该读写锁
read_unlock(lock);
在对共享资源进行读取之前,应该先调用读锁定函数锁定共享资源,完成之后再调用读解锁函数释放共享资源
举例:
read_lock(&tasklist_lock);
read_unlock(&tasklist_lock);
write_lock(lock); //如果不能获得,它将自旋,直到获得该读写锁
write_unlock(lock);
在对共享资源进行写操作之前,应该先调用写锁定函数锁定共享资源,完成之后再调用写解锁函数释放共享资源
举例:
write_lock(&tasklist_lock);
write_unlock(&tasklist_lock);
read_trylock(lock);
write_trylock(lock);
举例:
rwlock_t lock;
if (!read_trylock(&lock))
if (!write_trylock(&lock))
read_lock_irq(lock); //读者获取读写锁,并禁止本地中断
read_unlock_irq(lock); //读者释放读写锁,并使能本地中断
举例:
read_lock_irq(&tasklist_lock);
read_unlock_irq(&tasklist_lock);
write_lock_irq(lock); //写者获取读写锁,并禁止本地中断
write_unlock_irq(lock);
举例:
write_lock_irq(&tasklist_lock);
write_unlock_irq(&tasklist_lock);
read_lock_irqsave(lock, flags); //读者获取读写锁,同时保存中断标志,并禁止本地中断
read_unlock_irqrestores(lock,flags); //读者释放读写锁,同时恢复中断标志,并使能本地中断
举例:
unsigned long flags;
read_lock_irqsave(&tasklist_lock, flags);
read_unlock_irqrestore(&tasklist_lock, flags);
write_lock_irqsave(lock,flags); //写者获取读写锁,同时保存中断标志,并禁止本地中断
write_unlock_irqstore(lock,flags);
举例:
unsigned long flags;
write_lock_irqsave(&tasklist_lock, flags);
write_unlock_irqrestore(&tasklist_lock, flags);
read_lock_bh(lock); //读者获取读写锁,并禁止本地软中断
read_unlock_bh(lock);
write_lock_bh(lock); //写者获取读写锁,并禁止本地软中断
write_unlock_bh(lock);
举例:
rwlock_t lock; //定义rwlock
rwlock_init(&lock); //初始化rwlock
//读时获取锁
read_lock(&lock);
...临界区...
read_unlock(&lock);
//写时获取锁
write_lock_irqsave(&lock, flags);
…临界区...
write_unlock_irqrestore(&lock, flags);