单核与多核上锁的区别

参考:1、《Linux Kernel Development》3ed_CN  p131-p140

            2、2.6.34

单核:

//锁的数据类型实现
typedef struct { } arch_spinlock_t;

typedef struct raw_spinlock {
    arch_spinlock_t raw_lock;
}raw_spinlock_t;

typedef struct spinlock {
    union {
        struct raw_spinlock rlock;
    }; //以我对C的了解,这种定义方式还是第一次见到(以前见到,也没留意过),这个union的联合体的最后竟然没有变量名称,即union {xxxxx} var_name; gcc后指定为c89标准也能正常编译与运行
       //还有就是可以通过&lock->rlock,直接获得访问rlock的地址, 原来都是通过&lock->var_name.rlock来操作完成
       //此处加个变量名,反而显得累赘,而且如果加上变量明就必须通过变量名访问。 }spinlock_t;
//锁是空的

//spin_lock的实现

#define __acquire(x) (void)0
#define __LOCK(lock) 
    do {preempt_disable(); __acquire(lock); (void)(lock); } while(0)
#define _raw_spin_lock(lock) __LOCK(lock)
#define raw_spin_lock(lock) _raw_spin_lock(lock)


static inline void spin_lock(spinlock_t *lock)
{
    raw_spin_lock(&lock->rlock);
}

//单核是否支持抢占在锁上的区别:

#ifdef CONFIG_PREEMPT
#define preempt_disable() 
    do{ 
        inc_preempt_count(); 
        barrier(); 
    } while(0)
#else
#define preempt_disable() do { } while(0)

多核:

//锁的数据类型实现
typedef struct {
    volatile unsigned int lock;
} arch_spinlock_t;

typedef struct raw_spinlock {
    arch_spinlock_t raw_lock;
} raw_spinlock_t;

typedef struct spinlock {
    union {
        struct raw_spinlock rlock;
    };
} spinlock_t;

//spin_lock的实现

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
    unsigned long tmp;

    __asm__ __volatile__(
    "1: ldrex   %0, [%1]      
	"
    "   teq     %0, #0        
	"
    "   strexeq %0, %2, [%1]  
	"
    "   teqeq   %0, #0        
	"
    "   bne     1b                "
    : "=&r" (tmp)
    : "r" (&lock->lock), "r"(1)
    : "cc"
    smp_mb();
}

static inline void do_raw_spin_lock(raw_spinlock_t *lock)
{
    arch_spin_lock(&lock->raw_lock);
}

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
    preempt_disable();
    do_raw_spin_lock(lock);
}

static inline void spin_lock(spinlock_t *lock)
{
    raw_spin_lock(&lock->rlock);
}

附:

1、同步,锁的问题,我认为发生在(1)进程与进程之间;  (2)中断与进程之间;  (3)中断与中断之间(细分上半部、下半部)

1)如果进程上下文核一个下半部共享数据,在访问这些数据之前,需要禁止下半部的处理并得到锁的使用权。做这些是为了本地和SMP的保护并且防止死锁的出现。(p_128)

(2)如果中断上下文和一个下半部共享数据,在访问数据之前,需要禁止禁止中断并得到锁的使用权。做这些是为了本地和SMP的保护并且防止死锁的出现。(p_128)

(3)如若在一段内核代码操作某资源的时候系统产生了一个中断,而该中断的处理程序还要访问这一个资源,这就是一个bug。(p_135最后)

    关于(3),经讨论使用出使用spin_lock_irqsave;
    其它的也应有拌饭解决,以后会讲明。

2、记下书中提出的几条建议:

1)最开始设计的时候就要考虑加入锁,而不是事后才想到。如果代码已经写好,再在其中找到需要上锁的部分并向其中追加锁,是非常困难的,结果也往往也不尽如人意。避免这种亡羊补牢的做法是:在编写代码的开始阶段就要设计恰当的锁。(p_136中间)

(2lock contention(锁的争用):是指当锁正在被占用时,有其它线程试图获得该锁。锁处于高度争用的状态是指有多个其他线程在等待获得该锁。(p_138最后)

(3)被保护数据的规模描述了锁的粒度,细粒度的锁保护小块数据,过粗的锁保护大块数据。(p_139中间)                       
    当锁争用严重时,加锁太粗会降低可扩展性;而锁争用不明显时,加锁过细会加大系统开销,带来浪费,这两种情况都会造成系统性能下降。(p_139最后)

还是谈下spin_unlock,不然总缺了什么。

单核上:

static inline void spin_unlock(spinlock_t *lock)
{
        raw_spin_unlock(&lock->rlock);                                                                             
}

#define raw_spin_unlock(lock)           _raw_spin_unlock(lock)                                                     

#define _raw_spin_unlock(lock)                  __UNLOCK(lock)                                             

#define __UNLOCK(lock)                                                                                            
  do { preempt_enable(); __release(lock); (void)(lock); } while (0)

#ifdef CONFIG_PREEMPT

#define preempt_enable_no_resched()                                                                               
do { 
        barrier(); 
        dec_preempt_count(); 
} while (0)


#define preempt_check_resched()                                                                                   
do { 
        if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) 
                preempt_schedule(); 
} while (0)

//可能触发内核态抢占
#define preempt_enable() 
do { 
        preempt_enable_no_resched(); 
        barrier(); 
        preempt_check_resched(); 
} while (0)    

#else

#define preempt_enable()                do { } while (0)                                                           

#endif                                                                                                    

 多核:

static inline void spin_unlock(spinlock_t *lock)
{
        raw_spin_unlock(&lock->rlock);                                                                             
}

#define raw_spin_unlock(lock)           _raw_spin_unlock(lock)                                                     

#define _raw_spin_unlock(lock) __raw_spin_unlock(lock)     

static inline void dsb_sev(void)                                                                                   
{
        __asm__ __volatile__ (
                "dsb
"
                "sev"
        );
}
 
static inline void arch_spin_unlock(arch_spinlock_t *lock)                                                         
{
        smp_mb();

        __asm__ __volatile__(
"       str     %1, [%0]
"
        :       
        : "r" (&lock->lock), "r" (0)
        : "cc");
                
        dsb_sev();
}       

void do_raw_spin_unlock(raw_spinlock_t *lock)                                                                      
{
//        debug_spin_unlock(lock);
        arch_spin_unlock(&lock->raw_lock);
}

static inline void __raw_spin_unlock(raw_spinlock_t *lock)                                                         
{
//        spin_release(&lock->dep_map, 1, _RET_IP_);
        do_raw_spin_unlock(lock);
        preempt_enable(); //与单核抢占情形相同
}