进程间同步——自旋锁

http://blog.chinaunix.net/uid-26990992-id-3264808.html

自旋锁是为smp架构而设计的  保护较小临界区的 同步方法

在单核系统中且非抢占系统中,自旋锁退化为 无操作

在单核抢占系统中,退化为关闭抢占功能即可

  • 自旋锁需要做的工作

      从保证临界区访问原子性的目的来考虑,自旋锁应该阻止在代码运行过程中出现的任何并发干扰。这些“干扰”包括:

       1、中断,包括硬件中断和软件中断 (仅在中断代码可能访问临界区时需要)

         这种干扰存在于任何系统中,一个中断的到来导致了中断例程的执行,如果在中断例程中访问了临界区,原子性就被打破了。所以如果在某种中断例程中存在访问某个临界区的代码,那么就必须用spinlock保护。对于不同的中断类型(硬件中断和软件中断)对应于不同版本的自旋锁实现,其中包含了中断禁用和开启的代码。但是如果你保证没有中断代码会访问临界区,那么使用不带中断禁用的自旋锁API即可。 

      2、内核抢占(仅存在于可抢占内核中)

         2.6以后的内核中,支持内核抢占,并且是可配置的。这使UP系统和SMP类似,会出现内核态下的并发。这种情况下进入临界区就需要避免因抢占造成的并发,所以解决的方法就是在加锁时禁用抢占(preempt_disable(); ),在开锁时开启抢占(preempt_enable();注意此时会执行一次抢占调度) 。 

     3、 其他处理器对同一临界区的访问 (仅SMP系统) 

        SMP系统中,多个物理处理器同时工作,导致可能有多个进程物理上的并发。这样就需要在内存加一个标志,每个需要进入临界区的代码都必须检查这个标志,看是否有进程已经在这个临界区中。这种情况下检查标志的代码也必须保证原子和快速,这就要求必须精细地实现,正常情况下每个构架都有自己的汇编实现方案,保证检查的原子性。


  • 自旋锁操作组成

中断控制(仅在中断代码可能访问临界区时需要)

抢占控制(仅存在于可抢占内核中需要)

  • 自旋锁标志控制  (仅SMP系统需要)

  •    中断控制是按代码访问临界区的不同而在编程时选用不同的变体,有些API中有,有些没有。

     而抢占控制和自旋锁标志控制依据内核配置(是否支持内核抢占)和硬件平台(是否为SMP)的不同而在编译时确定。如果不需要,相应的控制代码就编译为空函数。 对于非抢占式内核,由自旋锁所保护的每个临界区都有禁止内核抢占的API,但是为空操作。由于UP系统不存在物理上的并行,所以可以阉割掉自旋的部分,剩下抢占和中断操作部分即可。  




最近在看宋宝华的《设备驱动开发详解》第二版,看到自旋锁的部分,有些疑惑,所以来请教下大家。

下面是我参考一些网络上的资料得出的一些想法,不知正确与否,记录下来大家讨论下:
(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可抢占或不可抢占内核,没有实现“自旋”,仅仅是“禁止内核抢占”,因此不会发生死锁,但是会发生无保护的重复进入临界区的情况(即无法实现保护临界区的功能)。

以上观点只是个人想法,不当之处,还请各位指出,谢谢。



单cpu 非抢占:  内核中的执行天然的隔绝了进程切换。要注意 自旋锁不能防止系统调用被 硬中断或者异常抢占去。

单cpu抢占:  spin_lock只 要关抢占,否则在本cpu起不到任何 锁的作用。 这要求临界区尽量的短小

多cpu 抢占: 自选就意味着核间呼斥,本cpu上的进程间互斥,由其关抢占性质实现 !

  多cpu 非抢占: 自选就意味着核间呼斥就可以了 !

怎么会发生死锁呢?
咱们分情况来看一下:
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上的自旋锁实际没有“自旋功能”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值