1. 单处理器不可抢占(!CONFIG_SMP && !CONFIG_PREEMPT):
这种配置下,在内核中与spinlock相关的具体实现如下
#define _spin_lock(lock)
\
do { \
preempt_disable(); \空
_raw_spin_lock(lock); \空
__acquire(lock); \空
} while(0)
#define preempt_disable()
do { } while (0)
#define _raw_spin_lock(lock)
do { (void)(lock); } while(0)
即,实际上此时spin_lock()是空操作。
a. 对于spinlock来说,在此种配置下,正常情况:当前进程只能够被中断抢占(如果使用spin_lock_irq()甚至中断都不能够抢占),其他任何进程都不能换入,直到本进程完成临界区的执行。如果持有spinlock时睡眠:则会换入其他进程,可能对临界区数据进行修改,根本上破坏了使用锁的目的。
b. 对于信号量来说,在此种配置下,进程持有信号量然后睡眠,即使换入的其他进程想对临界区做修改,也因为信号量的阻隔不能成立。
spin_lock()在这种配置下的操作实际上是做了对lock的原子减1,特点是在自旋等待获取lock时不可被抢占。
a. 对于spinlock来说,正常情况:当前进程在本处理器上只能被中断抢占,在其他处理器上若有进程想要访问该临界区,则由于试图获取被当前进程持有的lock而自旋等待,直到本进程执行完临界区,其他处理器上的等待进程才能进入临界区。如果当前进程在持有spinlock的时候睡眠:则本处理器上换入了其他进程,如果之后换入了一个想要获取同一个自旋锁执行同一段临界区的进程,则会停在自旋检测lock的值处,若多处理器上均换入了这样的进程,而原始的进程始终没得到机会执行并跳出临界区,则此时系统会死锁崩溃。
b. 对于信号量来说:假设当前进程持有信号量然后睡眠,因为其他试图执行相同临界区的进程在获取信号量的时候不会自旋等待,这些进程会直接把自己放入等待队列然后调度自己,等待原始进程在执行完临界区之后做唤醒操作,所以不会产生死锁。