Linux内核中的互斥操作(2)——自旋锁

*本篇来看看多次在内核中出现的spin_lock——自旋锁,到底是个什么东西。。。→_→*

  1. 内核中的spin_lock()

    • spin_lock()源代码
    
    static inline void spin_lock(spinlock_t *lock)//自旋锁的类型定义见下方
        {
        #if SPINLOCK_DEBUG
            __label__ here;
        here:
            if (lock->magic != SPINLOCK_MAGIC) {
        printk("eip: %p\n", &&here);
                BUG();
            }
        #endif
            __asm__ __volatile__(
                spin_lock_string//进入宏函数spin_lock_string,传参数lock->lock
                :"=m" (lock->lock) : : "memory");
        }
    • 自旋锁的类型定义
    typedef struct {
            volatile unsigned int lock;//不考虑调试时,自旋锁就是一个无符号整形,volatile保证编译器不进行过度优化
        #if SPINLOCK_DEBUG
            unsigned magic;
        #endif
        } spinlock_t;
    • spin_lock_string宏函数
    
    #define spin_lock_string \
    
        "\n1:\t" \
        "lock ; decb %0\n\t" \ //decb指令涉及读-改-写操作,所以lock总线保证该条指令的原子性,%0就是传入的参数lock->lock,decb指令将lock->lock减1,结果非负表示加锁成功,直接返回
        "js 2f\n" \ 
        ".section .text.lock,\"ax\"\n" \
        "2:\t" \ //结果为负,循环测试lock->lock的值
        "cmpb $0,%0\n\t" \ //将lock->lock的值与0比较
        "rep;nop\n\t" \
        "jle 2b\n\t" \ //当lock->lock小于等于0时,继续循环测试
        "jmp 1b\n" \ //当lock->lock大于0时,跳转到标号1,获取自旋锁
        ".previous"

    从代码中得知,如果lock->lock小于等于0,那么就一直循环测试其值,直到lock->lock大于0。这就相当于让CPU一直空转,做无用功,因此自旋锁应用的地方不能加锁时间太长,否则就会浪费资源。

  2. 内核中的spin_unlock()

    • spin_unlock()源代码
    static inline void spin_unlock(spinlock_t *lock)
    {
    
    #if SPINLOCK_DEBUG
    
        if (lock->magic != SPINLOCK_MAGIC)
            BUG();
        if (!spin_is_locked(lock))
            BUG();
    
    #endif
    
        __asm__ __volatile__(
            spin_unlock_string//调用spin_unlock_string宏函数,传参数lock->lock
            :"=m" (lock->lock) : : "memory");
    }
    • spin_unlock_string宏函数
    
    #define spin_unlock_string \
    
        "movb $1,%0" //%0就是传入的参数lock->lock,movb指令将lock->lock置为1,movb指令本身就是原子操作,所以不需要lock总线
  3. 内核中的自旋锁具体应用的类型

    • 加锁操作
    
    
    #define spin_lock_irqsave(lock, flags)      do { local_irq_save(flags);       spin_lock(lock); } while (0)
    
    
    #define spin_lock_irq(lock)         do { local_irq_disable();         spin_lock(lock); } while (0)
    
    
    #define spin_lock_bh(lock)          do { local_bh_disable();          spin_lock(lock); } while (0)
    
    
    • 去锁操作
    
    
    #define spin_unlock_irqrestore(lock, flags) do { spin_unlock(lock);  local_irq_restore(flags); } while (0)
    
    
    #define spin_unlock_irq(lock)           do { spin_unlock(lock);  local_irq_enable();       } while (0)
    
    
    #define spin_unlock_bh(lock)            do { spin_unlock(lock);  local_bh_enable();        } while (0)
    
    
    • 不同操作之间的异同

    同:加锁和去锁操作中都是分为两部分,即先执行local_操作——关闭或开启本处理器上的中断响应,再执行_lock操作——防止来自其他处理器的干扰。

    异:主要区别就在于如何关闭本处理器上的中断响应

    /* For spinlocks etc */
    
    #define local_irq_save(x)   __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory") //通过cli指令关闭中断,且将本处理器的状态标识寄存器通过push和pop操作,保存到参数x中,以便去锁时恢复。状态标志寄存器中的IF标志位反映当前中断的开关状态
    
    
    #define local_irq_restore(x)    __restore_flags(x) //去锁时恢复标识寄存器
    
    
    #define local_irq_disable() __cli() //直接将标识寄存器的IF标志位清0
    
    
    #define local_irq_enable()  __sti()
    

*码完吃饭。。。下午继续最后的read_lock和write_lock。。。→_→*

猜你喜欢

转载自blog.csdn.net/kongkongkkk/article/details/76609933
今日推荐