今天把这两个锁的内核实现源码重新捋了一遍,基于liunx2,6.0,直接粘注释版: 核心文件,x86下实现的spinlock
代码语言:javascript复制#ifndef __ASM_SPINLOCK_H
#define __ASM_SPINLOCK_H
#include <asm/atomic.h>
#include <asm/rwlock.h>
#include <asm/page.h>
#include <linux/config.h>
extern int printk(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
/*
* Your basic SMP spinlocks, allowing only a single CPU anywhere
*/
typedef struct {
//自旋锁为无符号的整型变量 volatile保证变量都从内存中获取,不要缓存在寄存器里
volatile unsigned int lock;
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned magic;
#endif
} spinlock_t;
//定义一个spinlock的魔数,用来调试用
#define SPINLOCK_MAGIC 0xdead4ead
#ifdef CONFIG_DEBUG_SPINLOCK
#define SPINLOCK_MAGIC_INIT , SPINLOCK_MAGIC
#else
#define SPINLOCK_MAGIC_INIT /*如果没有开启调试状态将什么也没有 */
#endif
//创建一个值为1的自旋锁
#define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 SPINLOCK_MAGIC_INIT }
//初始化自旋锁 x是一根指针 所以解引用
#define spin_lock_init(x) do { *(x) = SPIN_LOCK_UNLOCKED; } while(0)
/*
* Simple spin lock operations. There are two variants, one clears IRQ's
* on the local processor, one does not.
* 简单的自旋锁操作。有两种变体,一种清除本地处理器上的IRQ,另一种不清除。
*
* We make no fairness assumptions. They have a cost.
* 我们没有做出公平的假设(非公平)。它们是有代价的
*/
//判断自旋锁是否被锁定 先通过取地址拿到spinlock里的lock 再转为字符,再解引用判断是否小于0
#define spin_is_locked(x) (*(volatile signed char *)(&(x)->lock) <= 0)
//等待自旋锁释放,barrier()保证禁止编译器任意排序
#define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x))
//获取自旋锁内联汇编代码,这里只是code部分,剩下在用的时候肯定是有输出数和输入数的
#define spin_lock_string
"1:" //1位置
"lock ; decb %0" //对lock变量的值进行自减,如果lock变量是0或者小于0,再减岂不是直接就小于0了吗,所以底下js判断是否符号位为1,也就是小于0
"js 2f" //f是forward,往后跳,因为2:确实在后面,如果小于0,跳转到前面的2处 js判断的是符号位是否为1,为1当然就小于0啦
LOCK_SECTION_START("") //涉及到ELF的知识
"2:"
"rep;nop" //repeat空操作
"cmpb $0,%0" //比较lock的值是否为0,%0可以从后面的代码看出,是输出参数,是lock变量 cmpb的b代表比一个字节,l代表4字节
"jle 2b" //b是backward,往前跳,jle代表小于或等于0,继续2处
"jmp 1b" //jmp:无条件转移到指定内存地址,否则跳回到1处去进行减一操作,这也不一定还能拿到哦,还得判断,如果成功就拿到了锁!!!!!
LOCK_SECTION_END //".previousnt"
/*
* This works. Despite all the confusion.
* 这很有效。尽管如此混乱。
* (except on PPro SMP or if we are using OOSTORE)
* (PPro errata 66, 92)
*/
#if !defined(CONFIG_X86_OOSTORE) && !defined(CONFIG_X86_PPRO_FENCE)
#define spin_unlock_string //这里和lock_string是不一样的,直接就把输入输出和clobber都填好了
"movb $1,%0" //把1设置为lock的值
:"=m" (lock->lock) : : "memory" //static spinlock_t lock;这里的lock是一个spinlock_t,所以指针再指向spinlock里的lock
static inline void _raw_spin_unlock(spinlock_t *lock)
{
#ifdef CONFIG_DEBUG_SPINLOCK
if (lock->magic != SPINLOCK_MAGIC)
BUG();
if (!spin_is_locked(lock))
BUG();
#endif
__asm__ __volatile__(
spin_unlock_string
);
}
#else
#define spin_unlock_string
"xchgb �, %1"
:"=q" (oldval), "=m" (lock->lock)
:"0" (oldval) : "memory"
static inline void _raw_spin_unlock(spinlock_t *lock)
{
char oldval = 1;
#ifdef CONFIG_DEBUG_SPINLOCK
if (lock->magic != SPINLOCK_MAGIC)
BUG();
if (!spin_is_locked(lock))
BUG();
#endif
__asm__ __volatile__(
spin_unlock_string
);
}
#endif
static inline int _raw_spin_trylock(spinlock_t *lock)
{
char oldval;
__asm__ __volatile__(
"xchgb �,%1" //交换lock的值(%1)和oldval
:"=q" (oldval), "=m" (lock->lock) //q:将输入变量放入eax,ebx,ecx,edx中的一个
:"0" (0) : "memory"); //0:表示用它限制的操作数与某个指定的操作数(这里就是0)匹配
return oldval > 0; //大于0说明lock变量为1,那说明就没有人拿到这个锁,那么久尝试获得到了锁
}
static inline void _raw_spin_lock(spinlock_t *lock)
{
#ifdef CONFIG_DEBUG_SPINLOCK
__label__ here;
here:
if (lock->magic != SPINLOCK_MAGIC) {
printk("eip: %pn", &&here);
BUG();
}
#endif
__asm__ __volatile__(
spin_lock_string
:"=m" (lock->lock)//输出操作数列表为lock : : "memory");
}
/*
* Read-write spinlocks, allowing multiple readers
* but only one writer.
*
* NOTE! it is quite common to have readers in interrupts
* but no interrupt writers. For those circumstances we
* can "mix" irq-safe locks - any writer needs to get a
* irq-safe write-lock, but readers can get non-irqsafe
* read-locks.
*/
typedef struct {
volatile unsigned int lock;
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned magic;
#endif
} rwlock_t;//多个读者共享,写者互斥,和互斥自旋锁机构一模一样
#define RWLOCK_MAGIC 0xdeaf1eed
#ifdef CONFIG_DEBUG_SPINLOCK
#define RWLOCK_MAGIC_INIT , RWLOCK_MAGIC
#else
#define RWLOCK_MAGIC_INIT /* */
#endif
#define RW_LOCK_UNLOCKED (rwlock_t) { RW_LOCK_BIAS RWLOCK_MAGIC_INIT }//RW_LOCK_BIAS->0x0100 0000第七位
//初始化读写自旋锁
#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0)
#define rwlock_is_locked(x) ((x)->lock != RW_LOCK_BIAS)//RW_LOCK_BIAS代表没上锁(既没有读锁也没有写锁)
/*
* On x86, we implement read-write locks as a 32-bit counter
* with the high bit (sign) being the "contended" bit.
* 在x86上,我们将读写锁实现为32位计数器,高位(符号)为“争用”位。
*
* The inline assembly is non-obvious. Think about it.
*
* Changed to use the same technique as rw semaphores. See
* semaphore.h for details. -ben
*/
#endif
/* the spinlock helpers are in arch/i386/kernel/semaphore.c */
//helper__write_lock_failed和的实现去这个地方(arch/i386/kernel/semaphore.c)找,否则找不到
//获取读锁或者写锁失败后的helper实现
static inline void _raw_read_lock(rwlock_t *rw)
{
#ifdef CONFIG_DEBUG_SPINLOCK
if (rw->magic != RWLOCK_MAGIC)
BUG();
#endif
__build_read_lock(rw, "__read_lock_failed");//在读写锁文件rwlock.h里有相应实现
}
static inline void _raw_write_lock(rwlock_t *rw)
{
#ifdef CONFIG_DEBUG_SPINLOCK
if (rw->magic != RWLOCK_MAGIC)
BUG();
#endif
__build_write_lock(rw, "__write_lock_failed");
}
//读锁和写锁的释放也很简单,原子加1或者原子加0x0100 0000
#define _raw_read_unlock(rw) asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory")
#define _raw_write_unlock(rw) asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")
static inline int _raw_write_trylock(rwlock_t *lock)
{
atomic_t *count = (atomic_t *)lock;
if (atomic_sub_and_test(RW_LOCK_BIAS, count))
return 1;
atomic_add(RW_LOCK_BIAS, count);
return 0;
}
#endif /* __ASM_SPINLOCK_H */
rwlock.h核心文件,x86实现:
代码语言:javascript复制/* include/asm-x86_64/rwlock.h
*
* Helpers used by both rw spinlocks and rw semaphores.
*
* Based in part on code from semaphore.h and
* spinlock.h Copyright 1996 Linus Torvalds.
*
* Copyright 1999 Red Hat, Inc.
* Copyright 2001,2002 SuSE labs
*
* Written by Benjamin LaHaise.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _ASM_X86_64_RWLOCK_H
#define _ASM_X86_64_RWLOCK_H
#include <linux/stringify.h>
#define RW_LOCK_BIAS 0x01000000
#define RW_LOCK_BIAS_STR "0x01000000"
#define __build_read_lock_ptr(rw, helper)
asm volatile(LOCK "subl $1,(%0)" //获取读锁就是尝试在lock上减1,因为RW_LOCK_BIAS是非常大的一个数
// 只有有写锁的时候会直接减去0x01000000变为0,其他时候是不可能小于0的,所以没有写锁的情况下,去抢占这个读锁是没有问题的
"js 2f" //判断符号位是否为1,也即是否为负数,如果是负数,那么说明已经有写锁啦,那你只能去2位置了
"1:"
LOCK_SECTION_START("")
"2: call " helper "" //调用helper方法,helper即为__read_lock_failed,下面把这段实现摘抄出来了,我没有获取到锁,只好调用这个方法
"jmp 1b"
LOCK_SECTION_END
::"a" (rw) : "memory") //a:将输入变量放入eax
//这段代码是从i386的semaphore下摘出来的
asm(
"__read_lock_failed:"
LOCK "incl (�x)"//原子性增加eax寄存器中的值(也就是lock变量的值)
"1: rep; nop" //进行空操作,耗掉一点点时间
"cmpl $1,(�x)"// cmp影响符号位,lock变量和1相减如果是负数,符号位为1
"js 1b" //如果是负数那么就去1重新比较,直到可以获得读锁
LOCK "decl (�x)"//说明eax也就是lock大于等于1,进行相减再次判断是否为负数,因为可能两个线程同时走这一步,严谨!!!!!
"js __read_lock_failed"//负数说明又没抢到,继续循环吧
"ret"//抢到了,返回
);
#define __build_read_lock_const(rw, helper)
asm volatile(LOCK "subl $1,%0nt"
"js 2fn"
"1:n"
LOCK_SECTION_START("")
"2:tpushq %%raxnt"
"leaq %0,%%raxnt"
"call " helper "nt"
"popq %%raxnt"
"jmp 1bn"
LOCK_SECTION_END
:"=m" (*((volatile int *)rw))::"memory")
#define __build_read_lock(rw, helper) do {
if (__builtin_constant_p(rw))
__build_read_lock_const(rw, helper);
else
__build_read_lock_ptr(rw, helper); //走这里
} while (0)
#define __build_write_lock_ptr(rw, helper)
asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)" //核心,写锁只能有一个,上来直接把那个大数减完了
"jnz 2f" //看是否为0,不为0说明绝对有读锁在占着,直接失败去2处forward
"1:"
LOCK_SECTION_START("")
"2: call " helper "" 同读锁逻辑,截取helper如下
"jmp 1b"
LOCK_SECTION_END
::"a" (rw) : "memory")
//这段代码是从i386的semaphore下摘出来的
asm(
"__write_lock_failed:"
LOCK "addl $" RW_LOCK_BIAS_STR ",(�x)" //大同小异,先加后减
"1: rep; nopnt"
"cmpl $" RW_LOCK_BIAS_STR ",(�x)"
"jne 1b"
LOCK "subl $" RW_LOCK_BIAS_STR ",(�x)"
"jnz __write_lock_failed"
"ret"
);
#define __build_write_lock_const(rw, helper)
asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)nt"
"jnz 2fn"
"1:n"
LOCK_SECTION_START("")
"2:tpushq %%raxnt"
"leaq %0,%%raxnt"
"call " helper "nt"
"popq %%raxnt"
"jmp 1bn"
LOCK_SECTION_END
:"=m" (*((volatile long *)rw))::"memory")
#define __build_write_lock(rw, helper) do {
if (__builtin_constant_p(rw))
__build_write_lock_const(rw, helper);
else
__build_write_lock_ptr(rw, helper); //上读锁走这里
} while (0)
#endif
看注释可以很容易理解。