• 内核中自旋锁的使用


    自旋锁的一个特性就是忙等待,循环过程中会大量消耗CPU,可以在中断上下文中使用。

    只要有一个任务持有自旋锁,其他任务就可能在等待的时候自旋,使用自旋锁要确保不会长时间持有。

    在单处理器系统上,应该使用spin_lock_irqsave()和spin_lock_irqrestore(),禁用处理器上中断,防止中断并发。

    自旋锁与互斥锁的比较:
    1、互斥锁保护进程关键资源,而自旋锁保护IRQ处理程序的关键部分。
    2、互斥锁让竞争者在获得锁之前睡眠,而自旋锁在获得锁之前一直自旋循环(消耗CPU)
    3、自旋锁不能长时间持有,因为等待者在等待取锁期间会浪费CPU时间,互斥锁可以长时间持有,竞争者被放入等待队列中进入睡眠状态。

    持有自旋锁的线程被禁止抢占,自旋锁抢占者没有被禁止抢占。

    以下为一些常见自旋锁加解锁相关函数

    // 主要数据结构
    typedef struct spinlock {
    	union {
    		struct raw_spinlock rlock;
    
    #ifdef CONFIG_DEBUG_LOCK_ALLOC
    # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
    		struct {
    			u8 __padding[LOCK_PADSIZE];
    			struct lockdep_map dep_map;
    		};
    #endif
    	};
    } spinlock_t;
    
    // 静态初始化
    #define DEFINE_SPINLOCK(x)	spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
    
    // 动态初始化
    #define spin_lock_init(_lock)				\
    do {							\
    	spinlock_check(_lock);				\
    	raw_spin_lock_init(&(_lock)->rlock);		\
    } while (0)
    
    // 获取给定的自旋锁
    static __always_inline void spin_lock(spinlock_t *lock)
    {
    	raw_spin_lock(&lock->rlock);
    }
    // 释放锁给定的自旋锁
    static __always_inline void spin_unlock(spinlock_t *lock)
    {
    	raw_spin_unlock(&lock->rlock);
    }
    
    // 禁止软件中断并且获取给定的自旋锁
    static __always_inline void spin_lock_bh(spinlock_t *lock)
    {
    	raw_spin_lock_bh(&lock->rlock);
    }
    // 释放给定的自旋锁并且启动软件中断
    static __always_inline void spin_unlock_bh(spinlock_t *lock)
    {
    	raw_spin_unlock_bh(&lock->rlock);
    }
    
    // 禁止本地处理器上的中断,并且不保存之前的中断状态的标识
    static __always_inline void spin_lock_irq(spinlock_t *lock)
    {
    	raw_spin_lock_irq(&lock->rlock);
    }
    static __always_inline void spin_unlock_irq(spinlock_t *lock)
    {
    	raw_spin_unlock_irq(&lock->rlock);
    }
    
    // 禁止本地处理器上的中断,并且保存之前的中断状态的标识
    // flags类型为:unsigned long
    #define spin_lock_irqsave(lock, flags)				\
    do {								\
    	raw_spin_lock_irqsave(spinlock_check(lock), flags);	\
    } while (0)
    static __always_inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
    {
    	raw_spin_unlock_irqrestore(&lock->rlock, flags);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    自旋锁初始化的两种方式:
    1、动态初始化

    spinlock_t lock;
    spin_lock_init (&lock);
    
    • 1
    • 2

    2、静态初始化

    DEFINE_SPINLOCK(lock);
    
    • 1

    以下为一些自旋锁的相关辅助函数

    // 判断自旋锁是否已经被持有
    /**
     * spin_is_locked() - Check whether a spinlock is locked.
     * @lock: Pointer to the spinlock.
     *
     * This function is NOT required to provide any memory ordering
     * guarantees; it could be used for debugging purposes or, when
     * additional synchronization is needed, accompanied with other
     * constructs (memory barriers) enforcing the synchronization.
     *
     * Returns: 1 if @lock is locked, 0 otherwise.
     *
     * Note that the function only tells you that the spinlock is
     * seen to be locked, not that it is locked on your CPU.
     *
     * Further, on CONFIG_SMP=n builds with CONFIG_DEBUG_SPINLOCK=n,
     * the return value is always 0 (see include/linux/spinlock_up.h).
     * Therefore you should not rely heavily on the return value.
     */
    static __always_inline int spin_is_locked(spinlock_t *lock)
    {
    	return raw_spin_is_locked(&lock->rlock);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    自旋锁使用流程

    spinlock_t my_spin_lock;
    
    spin_lock_init(my_spin_lock);
    spin_lock(&my_spin_lock);
    spin_unlock(&my_spin_lock);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    读写自旋锁的使用场景:对临界区读多写少的情况。

    // 主要数据结构
    typedef struct {
            arch_rwlock_t raw_lock;
    #ifdef CONFIG_DEBUG_SPINLOCK
            unsigned int magic, owner_cpu;
            void *owner;
    #endif
    #ifdef CONFIG_DEBUG_LOCK_ALLOC
            struct lockdep_map dep_map;
    #endif        
    } rwlock_t;
    
    // 读写自旋锁的初始化
    # define rwlock_init(lock)                                      \
    do {                                                            \
            static struct lock_class_key __key;                     \
                                                                    \
            __rwlock_init((lock), #lock, &__key);                   \
    } while (0)
    
    // 与普通自选锁一样,也存在以下函数
    #define write_lock(lock)        _raw_write_lock(lock)
    #define read_lock(lock)         _raw_read_lock(lock)
    #define read_lock_irq(lock)             _raw_read_lock_irq(lock)
    #define read_lock_bh(lock)              _raw_read_lock_bh(lock)
    #define write_lock_irq(lock)            _raw_write_lock_irq(lock)
    #define write_lock_bh(lock)             _raw_write_lock_bh(lock)
    #define read_unlock(lock)               _raw_read_unlock(lock)
    #define write_unlock(lock)              _raw_write_unlock(lock)
    #define read_unlock_irq(lock)           _raw_read_unlock_irq(lock)
    #define write_unlock_irq(lock)          _raw_write_unlock_irq(lock)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    Postgresql中的C/C++混编(JIT)
    EasySwipeMenuLayout - 独立的侧滑删除
    # 智慧社区管理系统-核心业务管理-03投诉信息
    uniapp从零到一的学习商城实战
    (附源码)spring boot校园拼车微信小程序 毕业设计 091617
    OpenWRT篇——篇:Ubox——list.h
    LeetCode_区间问题_中等_795.区间子数组个数
    【JavaEE】Servlet(创建Maven、引入依赖、创建目录、编写及打包、部署和验证、smart Tomcat)
    Python模块:模块搜索顺序、内置属性(__file__和__name__)、开发原则
    .NET合并程序集(多个dll/exe合并成一个dll)
  • 原文地址:https://blog.csdn.net/qq_42931917/article/details/127832737