现在的位置: 首页 > 综合 > 正文

自旋锁与互斥锁

2014年11月13日 ⁄ 综合 ⁄ 共 3439字 ⁄ 字号 评论关闭

自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被

别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放

了锁,"自旋"一词就是因此而得名。其作用是为了解决某项资源的互斥使用。因

为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。虽然它的效率

比互斥锁高,但是它也有些不足之处: 

1、自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占

用着CPU,如果不能在很短的时 间内获得锁,这无疑会使CPU效率降低。 

2、在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其

他函数也可能造成死锁,如 copy_to_user()、copy_from_user()、kmalloc()

等。 

因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正

需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于

锁使用者保持锁时间比较短的情况下。 

自旋锁的用法如下: 

首先定义:spinlock_t x; 

然后初始化:spin_lock_init(spinlock_t *x); //自旋锁在真正使用前必须先

初始化 

在2.6.11内核中将定义和初始化合并为一个宏:DEFINE_SPINLOCK(x) 

 

获得自旋锁:spin_lock(x);   //只有在获得锁的情况下才返回,否则一直“自

旋” 

spin_trylock(x); //如立即获得锁则返回真,否则立即返回假 

释放锁:spin_unlock(x); 

 

结合以上有以下代码段: 

 

spinlock_t lock;        //定义一个自旋锁 

spin_lock_init(&lock); 

spin_lock(&lock);     

.......        //临界区 

spin_unlock(&lock);   //释放锁 

 

还有一些其他用法: 

spin_is_locked(x) 

//  该宏用于判断自旋锁x是否已经被某执行单元保持(即被锁),如果

是,   返回真,否则返回假。 

spin_unlock_wait(x) 

//  该宏用于等待自旋锁x变得没有被任何执行单元保持,如果没有任何执行

单元保持该自旋锁,该宏立即返回,否 

//将循环    在那里,直到该自旋锁被保持者释放。 

 

spin_lock_irqsave(lock, flags) //  该宏获得自旋锁的同时把标志寄存器的值保存到变量flags中并失效本

地中//断。相当于:spin_lock()+local_irq_save() 

spin_unlock_irqrestore(lock, flags) 

//  该宏释放自旋锁lock的同时,也恢复标志寄存器的值为变量flags保存

的//值。它与spin_lock_irqsave配对使用。 

//相当于:spin_unlock()+local_irq_restore() 

 

spin_lock_irq(lock) 

//该宏类似于spin_lock_irqsave,只是该宏不保存标志寄存器的值。相

当         //于:spin_lock()+local_irq_disable() 

spin_unlock_irq(lock) 

//该宏释放自旋锁lock的同时,也使能本地中断。它与spin_lock_irq配对应

用。相当于: spin_unlock()+local_irq+enable() 

 

spin_lock_bh(lock) 

//  该宏在得到自旋锁的同时失效本地软中断。相当于: 

//spin_lock()+local_bh_disable() 

spin_unlock_bh(lock) 

//该宏释放自旋锁lock的同时,也使能本地的软中断。它与spin_lock_bh配对

//使用。相当于:spin_unlock()+local_bh_enable() 

 

spin_trylock_irqsave(lock, flags) 

//该宏如果获得自旋锁lock,它也将保存标志寄存器的值到变量flags中,并

且失//效本地中断,如果没有获得锁,它什么也不做。因此如果能够立即获得锁,

它等//同于spin_lock_irqsave,如果不能获得锁,它等同于spin_trylock。如

果该宏//获得自旋锁lock,那需要使用spin_unlock_irqrestore来释放。 

 

spin_trylock_irq(lock) 

//该宏类似于spin_trylock_irqsave,只是该宏不保存标志寄存器。如果该宏

获得自旋锁lock,需要使用spin_unlock_irq来释放。 

spin_trylock_bh(lock) 

//  该宏如果获得了自旋锁,它也将失效本地软中断。如果得不到锁,它什么

//也不做。因此,如果得到了锁,它等同于spin_lock_bh,如果得不到锁,它

等同//于spin_trylock。如果该宏得到了自旋锁,需要使用spin_unlock_bh来

释放。 

spin_can_lock(lock) 

//  该宏用于判断自旋锁lock是否能够被锁,它实际是spin_is_locked取反。

//如果lock没有被锁,它返回真,否则,返回 假。该宏在2.6.11中第一次被

定义,在//先前的内核中并没有该宏。 

 

互斥锁主要用于实现内核中的互斥访问功能。内核互斥锁是在原子 API 之上实

现的,但这对于内核用户是不可见的。对它的访问必须遵循一些规则:同一时间

只能有一个任务持有互斥锁,而且只有这个任务可以对互斥锁进行 解锁。互斥

锁不能进行递归锁定或解锁。一个互斥锁对象必须通过其API初始化,而不能使用memset或复制初始化。一个任务在持有互斥锁的时候是不能结束的。互斥锁

所使用的内存区域是不能被释放的。使用中的互斥锁是不能被重新初始化的。并

且互斥锁不能用于中断上下文。但是互斥锁比当前的内核信号量选项更快,并且

更加紧凑,因此如果它们满足您的需 求,那么它们将是您明智的选择。 

 

 

1、定义并初始化: 

struct mutex mutex; 

mutex_init(&mutex); 

 

直接定于互斥锁mutex并初始化为未锁定,己count为1,wait_lock为未上锁,

等待队列wait_list为空。 

2、获取互斥锁: 

(1)具体参见linux/kernel/mutex.c 

void inline fastcall __sched mutex_lock(struct mutex *lock); 

获取互斥锁。实际上是先给count做自减操作,然后使用本身的自旋锁进入临界

区操作。首先取得count的值,在将count置为-1,判断如果原来count的置

为1,也即互斥锁可以获得,则直接获取,跳出。否则进入循环反复测试互斥锁

的状态。在循环中,也是先取得互斥锁原来的状态,在将其之为-1,判断如果

可以获取(等于1),则退出循环,否则设置当前进程的状态为不可中断状态,解

锁自身的自旋锁,进入睡眠状态,待被在调度唤醒时,再获得自身的自旋锁,进

入新一次的查询其自身状态(该互斥锁的状态)的循环。 

(2)具体参见linux/kernel/mutex.c 

int fastcall __sched mutex_lock_interruptible(struct mutex *lock); 

和mutex_lock()一样,也是获取互斥锁。在获得了互斥锁或进入睡眠直到获得

互斥锁之后会返回0。如果在等待获取锁的时候进入睡眠状态收到一个信号(被

信号打断睡眠),则返回_EINIR。 

(3)具体参见linux/kernel/mutex.c 

int fastcall __sched mutex_trylock(struct mutex *lock); 

试图获取互斥锁,如果成功获取则返回1,否则返回0,不等待。 

3、释放互斥锁: 

具体参见linux/kernel/mutex.c 

void fastcall mutex_unlock(struct mutex *lock); 

释放被当前进程获取的互斥锁。该函数不能用在中断上下文中,而且不允许去释

放一个没有上锁的互斥锁。 

五、使用形式: 

struct mutex mutex; 

mutex_init(&mutex); /*定义*/ 

... 

mutex_lock(&mutex); /*获取互斥锁*/ 

... /*临界资源*/ 

mutex_unlock(&mutex); /*释放互斥锁*/  

抱歉!评论已关闭.