自旋锁介绍
自旋锁(spin lock)是一种典型的对临界资源进行互斥访问的手段。如果A执行单元首先进入例程,他将持有自旋锁,当B执行单元试图进入同一个例程时,将获悉自旋锁已经被持有,然后需要等待A单元释放后,方可进入。
相关操作函数
1.定义自旋锁
spinlock_t lock;
2.初始化自旋锁
spin_lock_init(lock);
3 获得自旋锁
spin_lock(lock);
该宏如果能够立即获得锁。它就马上返回;否则,他就将在其中自旋,直到该自旋锁的保持者释放。
spin_trylock(lock);
该宏尝试获得自旋锁lock,如果能够立即获得锁,它则返回true, 否则返回false,实际上并不在原地打转。
4.释放自旋锁
spin_unlock(lock);
自旋锁的一般使用方法:
spinlock_t lock;
spin_lock_init(lock);
spin_lock(lock);
....... /*临界区*/
spin_unlock(lock);
较重要知识点
1.自旋锁主要针对于 SMP或者 单CPU但内核可抢占的情况,对于单CPU和内核不支持抢占的系统,自旋锁退化为空操作。
2.在自旋锁的持有期间,对应内核的抢占将被禁止。即不会被别的进程打扰。
3.在自旋锁期间不能调用可能引起进程调度的函数比如说copy_from_user,kmalloc(),msleep()等。
如果线程A 在持有锁期间进入了休眠状态,那么线程A 会自动放弃CPU 使用权。线程B 开始运行,线程B也想要获取锁,但是此时锁被A 线程持有,而且内核抢占还被禁止了!线程B 无法被调度出去,那么线程A 就无法运行,锁也就无法释放,好了,死锁发生了!
4.在自旋锁期间,需要禁止中断。如果当前代码被中断打断,而中断处理程序中也请求了这个锁, 就会造成死锁。
5.自旋锁还会收到底半部(下半部)的影响。
为了解决上面的4.5点造成的问题,需要在使用自旋锁的同时,关闭掉该核心的中断功能或者底半部功能,由此得到了自旋锁的衍生函数如下:
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()
主要用法
1.一般在进程上下文中,调用spin_lock_irqsave() / spin_unlock_irqrestore() ;
2.一般在中断上下文中,调用spin_lock() / spin_unlock() ;
这样,不管是核内还是核间,都杜绝了并发的可能性。
使用注意
1.自旋锁适用环境为占锁时间较短的情况,因为CPU此时不做任何操作,只是在循环等待,如果自旋锁时间占用过长,会引起CPU效率降低。
2.错误使用自旋锁可能会造成死锁。 常见问题是递归使用自旋锁。
3.自旋锁锁定期间不能调用引起进程调度的函数。如:copy_from_user() 、copy_to_user()、Kmalloc()、和msleep() 等函数。可能引起内核的崩溃。
4. 自旋锁在编程时,编程者应该认为自己所使用的CPU是多核的情况。
读写自旋锁(新)
常规自旋锁不关心临界区究竟在做什么操作,不管读写都一视同仁。但实际上共享资源的并发访问,同时读取是没有什么问题的。只是不能同时写,或者同时读写。读写自旋锁就是为了解决这个问题。
1.定义和初始化读写自旋锁
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.读解锁
4.写锁定
5.写解锁