读了这个http://blog.csdn.net/qinzhonghello/article/details/3557055,对自旋锁有了更深的理解。
自旋锁可以使用在中断处理程序中(此处不能使用信号量,因为它会导致睡眠)。在中断处理程序中使用自旋锁时, 一定要在获取锁之前,首先禁本地中断 (在当前处理器上的中断请求),否则,中断处理程序就会打断正持有锁的内核代码,有可能会试图去争用这个已经被持有的自旋锁。注意,需要关闭的只是当前处理器上的中断,因为如果中断发生在不同的处理器上,即使中断处理程序在同一锁上的自旋,也不会妨碍锁的持有者最终释放锁。
如果不禁止中断的话,很可能该CPU上的其他线程会中断这个锁,并执行其他的代码。
函数spin_lock_irqsave()保存中断的当然状态,并禁止本地中断,然后再去获取指定的锁。
extern spinlock_t lock;
// ...
spin_lock(&lock);
// do something
spin_unlock(&lock);
extern spinlock_t lock;
// ...
spin_lock(&lock);
// do something
// interrupted by IRQ ...
// in IRQ
extern spinlock_t lock;
spin_lock(&lock);
extern spinlock_t lock;
// ...
cli; // disable interrupt on current CPU
spin_lock(&lock);
// do something
spin_unlock(&lock);
sti; // enable interrupt on current CPU
extern spinlock_t lock;
// ...
spin_lock_irq(&lock);
// do something
spin_unlock_irq(&lock);
// code 1
extern spinlock_t lock1;
// ...
spin_lock_irq(&lock);
// do something
spin_unlock_irq(&lock);
// code 2
extern spinlock_t lock2;
// ...
spin_lock_irq(&lock2);
// do something
spin_unlock_irq(&lock2);
spin_lock_irq(&lock1);
local_irq_save(flags);
spin_lock(&lock);
// do something
spin_unlock(&lock);
local_irq_restore(flags);
阅读(112) | 评论(0) | 转发(0) |
<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script> <script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
自旋锁可以使用在中断处理程序中(此处不能使用信号量,因为它会导致睡眠)。在中断处理程序中使用自旋锁时, 一定要在获取锁之前,首先禁本地中断 (在当前处理器上的中断请求),否则,中断处理程序就会打断正持有锁的内核代码,有可能会试图去争用这个已经被持有的自旋锁。注意,需要关闭的只是当前处理器上的中断,因为如果中断发生在不同的处理器上,即使中断处理程序在同一锁上的自旋,也不会妨碍锁的持有者最终释放锁。
如果不禁止中断的话,很可能该CPU上的其他线程会中断这个锁,并执行其他的代码。
函数spin_lock_irqsave()保存中断的当然状态,并禁止本地中断,然后再去获取指定的锁。
Spinlock的目的是用来同步SMP中会被多个CPU同时存取的变量。在Linux中,普通的spinlock由于不带额外的语义,是用起来反而要非常小心。
在Linux kernel中执行的代码大体分normal和interrupt context两种。tasklet/softirq可以归为normal因为他们可以进入等待;nested interrupt是interrupt context的一种特殊情况,当然也是interrupt context。Normal级别可以被interrupt抢断,interrupt会被另一个interrupt抢断,但不会被normal中断。各个 interrupt之间没有优先级关系,只要有可能,每个interrupt都会被其他interrupt中断。
我们先考虑单CPU的情况。在这样情况下,不管在什么执行级别,我们只要简单地把CPU的中断关掉就可以达到独占处理的目的。从这个角度来说,spinlock的实现简单地令人乍舌:cli/sti。只要这样,我们就关闭了preemption带来的复杂之门。
单CPU的情况很简单,多CPU就不那么简单了。单纯地关掉当前CPU的中断并不会给我们带来好运。当我们的代码存取一个shared variable时,另一颗CPU随时会把数据改得面目全非。我们需要有手段通知它(或它们,你知道我的意思)——spinlock正为此设。这个例子是我们的第一次尝试:
extern spinlock_t lock;
// ...
spin_lock(&lock);
// do something
spin_unlock(&lock);
他能正常工作吗?答案是有可能。在某些情况下,这段代码可以正常工作,但想一想会不会发生这样的事:
// in normal run level
extern spinlock_t lock;
// ...
spin_lock(&lock);
// do something
// interrupted by IRQ ...
// in IRQ
extern spinlock_t lock;
spin_lock(&lock);
喔,我们在normal级别下获得了一个spinlock,正当我们想做什么的时候,我们被interrupt打断了,CPU转而执行interrupt level的代码,它也想获得这个lock,于是“死锁”发生了!解决方法很简单,看看我们第二次尝试:
extern spinlock_t lock;
// ...
cli; // disable interrupt on current CPU
spin_lock(&lock);
// do something
spin_unlock(&lock);
sti; // enable interrupt on current CPU
在获得spinlock之前,我们先把当前CPU的中断禁止掉,然后获得一个lock;在释放lock之后再把中断打开。这样,我们就防止了死锁。事实上,Linux提供了一个更为快捷的方式来实现这个功能:
extern spinlock_t lock;
// ...
spin_lock_irq(&lock);
// do something
spin_unlock_irq(&lock);
如果没有nested interrupt,所有这一切都很好。加上nested interrupt,我们再来看看这个例子:
// code 1
extern spinlock_t lock1;
// ...
spin_lock_irq(&lock);
// do something
spin_unlock_irq(&lock);
// code 2
extern spinlock_t lock2;
// ...
spin_lock_irq(&lock2);
// do something
spin_unlock_irq(&lock2);
Code 1和code 2都可运行在interrupt下,我们很容易就可以想到这样的运行次序():
Code 1 Code 2
extern spinlock_t lock1;
// ...spin_lock_irq(&lock1);
extern spinlock_t lock2;
// ...spin_lock_irq(&lock1);
// do something
spin_unlock_irq(&lock2);
// do something
spin_unlock_irq(&lock1);
问题是在第二个spin_unlock_irq后这个CPU的中断已经被打开,“死锁”的问题又会回到我们身边!
解决方法是我们在每次关闭中断前纪录当前中断的状态,然后恢复它而不是直接把中断打开。
unsigned long flags;
local_irq_save(flags);
spin_lock(&lock);
// do something
spin_unlock(&lock);
local_irq_restore(flags);
Linux同样提供了更为简便的方式:
unsigned long flags;
spin_lock_irqsave(&lock, flags);
// do something
spin_lock_irqsave(&lock, flags);
// do something
spin_unlock_irqrestore(&lock, flags);
相关热门文章
给主人留下些什么吧!~~
评论热议