关于自旋锁与内核抢占的一些想法,大虾过来指点下。
本帖最后由 oceanljp 于 2011-06-14 17:39 编辑
最近在看宋宝华的《设备驱动开发详解》第二版,看到自旋锁的部分,有些疑惑,所以来请教下大家。
下面是我参考一些网络上的资料得出的一些想法,不知正确与否,记录下来大家讨论下:
(1) linux上的自旋锁有三种实现:
1. 在单cpu,不可抢占内核中,自旋锁为空操作。
2. 在单cpu,可抢占内核中,自旋锁实现为“禁止内核抢占”,并不实现“自旋”。
3. 在多cpu,可抢占内核中,自旋锁实现为“禁止内核抢占” + “自旋”。
(2) 关于抢占式内核与非抢占式内核:
在非抢占式内核中,如果一个进程在内核态运行,其只有在以下两种情况会被切换:
1. 其运行完成(返回用户空间)
2. 主动让出cpu(即主动调用schedule或内核中的任务阻塞——这同样也会导致调用schedule)
在抢占式内核中,如果一个进程在内核态运行,其只有在以下四种情况会被切换:
1. 其运行完成(返回用户空间)
2. 主动让出cpu(即主动调用schedule或内核中的任务阻塞——这同样也会导致调用schedule)
3. 当从中断处理程序正在执行,且返回内核空间之前(此时可抢占标志premptcount须为0) 。
4. 当内核代码再一次具有可抢占性的时候,如解锁及使能软中断等。
在宋宝华的书中,有提到在使用自旋锁时,要避免用来保护“包含引起阻塞的代码”,因为阻塞意味着要进行进程的切换。这点让我很迷惑。因为在可抢占式内核中使用自旋锁,是“禁止内核抢占”的,既然“禁止内核抢占”怎么又会发生进程的切换呢?
现在我是这么想的:禁止内核抢占只是关闭“可抢占标志”,而不是禁止进程切换。显式使用schedule或进程阻塞(此也会导致调用schedule)时,还是会发生进程调度的。
这里补充一些想法:宋宝华的书上说,在使用自旋锁保护临界区时,如临界区中因“包含引起阻塞代码”而引发阻塞,从而引起进程切换后,若另一进程企图获得本自旋锁,死锁会发生。
个人感觉,只有在多cpu,内核可抢占的情况会发生死锁。而在单cpu,内核可抢占或不可抢占的情况,不会发生死锁,但此时自旋锁失效(即无法实现保护临界区的功能)。这是因为多cpu可抢占内核实现了“自旋”,所以会导致死锁;而单cpu可抢占或不可抢占内核,没有实现“自旋”,仅仅是“禁止内核抢占”,因此不会发生死锁,但是会发生无保护的重复进入临界区的情况(即无法实现保护临界区的功能)。
以上观点只是个人想法,不当之处,还请各位指出,谢谢。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
阻塞当然意味着要进行进程切换,当前进程阻塞也就是说当前进程去sleep了,这时候kernel会调用scheduler来选择一个合适的进程进入running状态。这个过程和禁止抢占不矛盾。
使用自旋锁时,要避免用来保护“包含引起阻塞的代码”,我的理解是这样的:自旋锁的实现是“抢的到就继续,否则就自旋——空循环”,如果一个进程长时间持有自旋锁,将会导致其他争用该自旋锁的进程长时间处于自旋状态,而进程自旋的时候其实什么都不做,白白消耗其时间片,浪费CPU时间。所以使用自旋锁的一个原则就是不要长时间持有它。如果在持有自旋锁的期间进程sleep了,那将导致持有该锁的时间长度不可预期(鬼知道这个进程什么时候会被wake up),这显然会违反使用自旋锁的上述原则。
回复 2# guocslock
同意楼上第一个观点。
第二个观点,使用自旋锁时,避免用来保护“包含引起阻塞的代码”的主要原因,应该是避免在阻塞并发生进程调度后,再次进入临界区引起的“死锁”或“无保护重复进入临界区的错误”。“不长时间持有自旋锁”应该不是其主要原因。
而且如我在一楼所述,自旋锁的实现在不同的情况下是不同的,并不都是“抢的到就继续,否则就自旋——空循环”。应分开讨论。
在单cpu系统中的自旋锁,并不具备“自旋”功能。
单cpu 非抢占: 内核中的执行天然的隔绝了进程切换。要注意 自旋锁不能防止系统调用被 硬中断或者异常抢占去。
单cpu抢占: spin_lock只 要关抢占,否则在本cpu起不到任何 锁的作用。 这要求临界区尽量的短小
多cpu 抢占: 自选就意味着核间呼斥,本cpu上的进程间互斥,由其关抢占性质实现 !
多cpu 非抢占: 自选就意味着核间呼斥就可以了 !
回复 3# oceanljp
怎么会发生死锁呢?
咱们分情况来看一下:
1. 单核非抢占:此时spinlock什么都不做,如果持有锁的进程阻塞,内核会通过scheduler选择其他进程执行,可能会造成临界区的冲突;
2. 单核抢占:此时spinlock只是禁止抢占,防止持有锁的进程被抢占而引起的临界区访问冲突,但是如果持有锁的进程阻塞,内核会通过scheduler选择其他进程执行,也可能会造成临界区的冲突;
3. 多核非抢占:此时spinlock应该是“多个CPU对锁抢的到就继续,否则就空循环”,持有锁的进程阻塞会会使得其他CPU争用该锁的时候忙等,引起系统性能下降;
4. 多核抢占:此时spinlock首先禁止持有锁的进程所在的CPU抢占,然后“多个CPU对锁抢的到就继续,否则就空循环”,持有锁的进程阻塞的话,一方面在持有锁的CPU上可能会造成临界区的冲突,另一方面会使得其他CPU争用该锁的时候忙等,造成系统性能下降。
所有这四种情况应该都不会导致死锁的发生。
死锁是有可能发生的。
死锁发生在多核的情况,下面来分析一下:
首先,对于多核抢占与多核非抢占的情况,在使用自旋锁时,其情况基本是一致的。
因为在多核抢占的情况下,使用自旋锁会禁止内核抢占,这样多核抢占就相当于多核非抢占的情况。
那下面就只分析多核非抢占的情况。
假设系统有A,B两个CPU。
A上正在运行的a进程已获得自旋锁,并在临界区运行。
B上正在运行的b进程企图获得自旋锁,但由于自旋锁已被占用,于是b进程在B CPU上“自旋”空转。
这时,如果在A上的a进程因程序阻塞,而被休眠。接着A会切换运行另一进程c。
若这个进程c也企图获取自旋锁,c进程同样会因为锁已被占用,而在A上“自旋”空转。
这时候,A上的a进程与c进程就形成了死锁。a进程需要被c进程占用的CPU,c进程需要被a进程占用的锁。
至于在单cpu内核上不会出现上述情况,因为单cpu上的自旋锁实际没有“自旋功能”。
oh,楼上的兄弟,不得不再说一下, 自旋锁的临界区,禁止使用睡眠。 如果你想使用睡眠锁请使用 mutex, semaphore,rt_mutex 。
自旋锁有别于睡眠锁,就是为了提高多核条件下的运行效率。 自选锁临界区sleep,等于自残!
看到这种分类就烦,单核下明明就不是自旋锁非要用同一个名字,又赋予其不同的语义。
回复 7# tuibo
呵呵,多谢这位兄弟指导,我只是看到宋宝华书上说,在自旋锁的临界区阻塞会发生死锁,与我想的不大一致,拿出来和大家探讨探讨。
单核下应该没自旋锁吧,这个应该只在smp中的概念吧