Linux内核锁主要有自旋锁和信号量这两种。
自旋锁的概念:
在执行功能单元之前,如果调用了自旋锁获取函数,并且没有其他功能单元在保持该锁,那么该执行单元则会立即获取该锁;倘若有其他执行单元,正在保持该锁,则该正在获取锁操作的执行单元将会保持自旋的状态,直到锁保持单元释放了该锁为止。
另强调一点:所谓自旋,即为忙循环,该操作不会引起调用者睡眠。
自旋锁的目的:
自旋锁是为了防止多处理器并发而引入的一种锁,它在内核中,大量应用于中断处理部分。而对于单处理器来说,防止中断中的并发,可以简单地采用关闭中断的方式,即在标志寄存器中打开或者关闭中断标志位,不需要自旋锁。
自旋锁API:
struct spinlock lock; //声明一个锁lock
-
typedef struct spinlock {
-
union {
-
struct raw_spinlock rlock;
-
};
-
} spinlock_t;
-
-
typedef struct raw_spinlock {
-
arch_spinlock_t raw_lock;
-
} raw_spinlock_t;
-
-
typedef struct {
-
volatile unsigned int lock;
-
} arch_spinlock_t
我把里面的DEBUG预编译都删了,层层剥开spinlock结构体,原来就是一个volatile unsigned int 数据,内核真是千回百转,然而细细去追寻,却也别有一番回味。
spin_lock_init(lock) //该函数用于初始化自旋锁lock
-
#define spin_lock_init(_lock) \
-
do { \
-
spinlock_check(_lock); \
-
raw_spin_lock_init(&(_lock)->rlock); \
-
} while (0)
第一层,可见,原来spin_lock_init()是一个宏定义,“\”是续行符。那么再看do {} while(0)循环,很有意思,while(0)中,0为FALSE,在很多的操作系统内核源码中,都是以 #define FALSE 0 #define TRUE 1 的方式定义的。那么这个循环,就是直接退出循环咯,至于为什么会这样,有可能是历史原因,也有可能是出于对未来的考虑了,哈哈。
言归正传,spinlock_check(_lock)这一步,就是返回_lock参数,在源码中,暂时没见更大的用处。
-
static inline raw_spinlock_t *spinlock_check(spinlock_t *lock)
-
{
-
return &lock->rlock;
-
}
那么就运行raw_spin_lock_init();
-
# define raw_spin_lock_init(lock) \
-
do { *(lock) = __RAW_SPIN_LOCK_UNLOCKED(lock); } while (0)
-
-
#define __RAW_SPIN_LOCK_UNLOCKED(lockname) \
-
(raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)
-
-
#define __RAW_SPIN_LOCK_INITIALIZER(lockname) \
-
{ \
-
.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \
-
SPIN_DEBUG_INIT(lockname) \
-
SPIN_DEP_MAP_INIT(lockname) }
-
-
# define SPIN_DEBUG_INIT(lockname)
-
# define SPIN_DEP_MAP_INIT(lockname)
-
-
#define __ARCH_SPIN_LOCK_UNLOCKED { }
观察这段整理后的代码,可见,其实
raw_spin_lock_init这个宏,就是在初始化raw_lock为_ARCH_SPIN_LOCK_UNLOCKED,也就是未加锁的状态吗?可是,看到这个宏定义为什么是空的呢?前面说到过,对于单处理器来说,不需要用到自旋锁,那么的话,这个很可能就是传说中的单内核的代码,就是不需要用到自旋锁的。
这里补充一点,所谓多核处理器就是SMP(Symmetrical Multi-Processing),即为对称多核处理器。而单核处理器是UP(Uni-Processor)。看到这里,我才发现,自己看了UP下的自旋锁代码,不过没关系,既来之则安之,继续往下看。
spin_lock(lock); //获得自旋锁lock,只有获得后才会返回,否则自旋
-
static inline void spin_lock(spinlock_t *lock)
-
{
-
raw_spin_lock(&lock->rlock);
-
}
-
-
#define raw_spin_lock(lock) _raw_spin_lock(lock)
-
-
#define _raw_spin_lock(lock) __LOCK(lock)
-
-
#define __LOCK(lock) \
-
do { preempt_disable(); __acquire(lock); (void)(lock); } while (0)
-
-
/*about preempt_disable()*/
-
#define preempt_disable() do { } while (0)
-
-
/*about __acquire()*/
-
# define __acquire(x) (void)0
在这里,我们可以看到从raw_spin_lock()开始一系列的宏定义中,整个spin_lock的过程中,似乎都没干什么事。然而,在__acquire(lock); (void)(lock); 这两步中,虽然什么都没有做,但是其有一个地方值得我们关注,就是(void)0,这行语句的作用,大概是为了迎合编译器的需要吧,这是个人理解,如果有不对的地方,还请赐教。
spin_trylock(lock); //尝试获得自旋锁lock,成功获得返回真,否则返回假
spin_unlock(lock); //释放自旋锁lock
至于以上两个接口,在UP下的内核代码中,也与spinlock大同小异,基本上也是以相似的代码,啥也没干,有兴趣可以自己去看看。这篇blog写到这里,好像啥也没说。。。um,不过,既然写了就不删了,毕竟知道了UP下的自旋锁的代码形式,对我看SMP的代码也是有莫大助益的,哈哈,接下来就继续兴致盎然地真正研究下SMP的自旋锁代码了。
-
static inline void spin_lock(spinlock_t *lock)
-
{
-
raw_spin_lock(&lock->rlock);
-
}
-
-
#define raw_spin_lock(lock) _raw_spin_lock(lock)
-
-
#define _raw_spin_lock(lock) __raw_spin_lock(lock)
-
-
static inline void __raw_spin_lock(raw_spinlock_t *lock)
-
{
-
preempt_disable();
-
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
-
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
-
}
-
-
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
-
{
-
__acquire(lock);
-
arch_spin_lock(&lock->raw_lock);
-
}
-
-
#define arch_spin_lock(lock) arch_spin_lock_flags(lock, 0)
-
-
#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
-
-
static inline void arch_spin_lock(arch_spinlock_t *lock)
-
{
-
unsigned long tmp;
-
-
__asm__ __volatile__(
-
"1: ldrex %0, [%1]\n"
-
" teq %0, #0\n"
-
WFE("ne")
-
" strexeq %0, %2, [%1]\n"
-
" teqeq %0, #0\n"
-
" bne 1b"
-
: "=&r" (tmp)
-
: "r" (&lock->lock), "r" (1)
-
: "cc");
-
-
smp_mb();
-
}
在SMP的环境下,spinlock的运行过程是如上代码所示,主要想高透的,是最后的那一段汇编代码是什么意思。
阅读(605) | 评论(0) | 转发(0) |