• 同步 -- 自旋锁


    基础学习--原子操作

    1. typedef struct {
    2. int counter;
    3. } atomic_t;
    4. static __always_inline void
    5. atomic_set(atomic_t *v, int i)
    6. {
    7. instrument_atomic_write(v, sizeof(*v));
    8. raw_atomic_set(v, i);
    9. }
    10. static __always_inline void
    11. raw_atomic_set(atomic_t *v, int i)
    12. {
    13. arch_atomic_set(v, i);
    14. }
    15. #define arch_atomic_read(v) __READ_ONCE((v)->counter)
    16. #define arch_atomic_set(v, i) __WRITE_ONCE(((v)->counter), (i))
    17. #define __WRITE_ONCE(x, val)
    18. do {
    19. *(volatile typeof(x) *)&(x) = (val); //typeof返回变量的类型
    20. } while (0)

    结构体

    spinlock -> raw_spinlock -> arch_spinlock_t(qspinlock)

    1. typedef struct spinlock {
    2. union {
    3. struct raw_spinlock rlock; //自旋锁的核心成员是raw_spinlock锁
    4. };
    5. } spinlock_t;
    6. typedef struct raw_spinlock {
    7. arch_spinlock_t raw_lock; //raw_lock的核心成员是arch_spinlock_t,它与具体架构有关
    8. } raw_spinlock_t;
    9. //ARM64位架构中,为qspinlock
    10. typedef struct qspinlock {
    11. union {
    12. atomic_t val; //原子变量
    13. #ifdef __LITTLE_ENDIAN
    14. struct {
    15. u8 locked; //最优先持锁标志,即当unlock之后,这个位被置位的CPU最先持锁,1和0
    16. u8 pending; //表示这个锁是否被人持有,1被人持有,0无人持锁
    17. };
    18. struct {
    19. u16 locked_pending; //由locked 和 pending构成
    20. u16 tail; //由idx CPU构成,用来标识等待队列最后一个节点
    21. };
    22. } arch_spinlock_t;

    相关API的实现

    调用逻辑:

    spin_lock_* -> raw_spin_lock_* -> _raw_* -> arch_spin_lock(架构相关,qspinlock)

    spin_lock_init

    自旋锁的初始化:就是做个spinlock->raw_spinlock->arch_spinlock_t的转换,然后把arch_spinlock_t的val初始化为0

    1. include\linux\spinlock_types_up.h
    2. # define spin_lock_init(_lock)
    3. do {
    4. spinlock_check(_lock);
    5. *(_lock) = __SPIN_LOCK_UNLOCKED(_lock);
    6. } while (0)

    spin_lock

    加锁:直接加锁,如果成功则返回,失败,则进入qspinlock的具体实现方式上

    1. static __always_inline void spin_lock(spinlock_t *lock)
    2. {
    3. raw_spin_lock(&lock->rlock);
    4. }
    5. static inline void __raw_spin_lock(raw_spinlock_t *lock)
    6. {
    7. preempt_disable();//关抢占
    8. spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    9. }
    1. #define spin_lock_irqsave(lock, flags) \
    2. do { \
    3. raw_spin_lock_irqsave(spinlock_check(lock), flags); \
    4. } while (0)
    5. static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
    6. {
    7. unsigned long flags;
    8. local_irq_save(flags);//关本地中断,并保存中断状态
    9. preempt_disable();//关抢占
    10. spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    11. return flags;
    12. }
    1. #define arch_spin_lock(l) queued_spin_lock(l)
    2. #define arch_spin_trylock(l) queued_spin_trylock(l)
    3. #ifndef queued_spin_lock
    4. static __always_inline void queued_spin_lock(struct qspinlock *lock)
    5. {
    6. int val = 0;
    7. if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)))
    8. return;
    9. queued_spin_lock_slowpath(lock, val);//这里就是直接获取锁失败的情况,需要自旋等待了。
    10. }

    qspinlock

    qspinlock的实现是建立在MCS锁的理论基础上。

    1. struct mcs_spinlock {
    2. struct mcs_spinlock *next;
    3. int locked;
    4. };

    mcs_spinlock中next成员就是构建单链表的基础,spin等锁的操作只需要将所属自己CPU的mcs_spinlock结构体加入单链表尾部,然后spin,直到自己的mcs_spinlock的locked成员置1(locked初始值是0)。unlock的操作也很简单,只需要将解锁的CPU对应的mcs_spinlock结构体的next域的lock成员置1,相当于通知下一个CPU退出循环。

    以4个CPU的系统为例说明。首先CPU0申请spinlock时,发现链表是空,并且锁是释放状态。所以CPU0获得锁。

    CPU1继续申请spinlock,需要spin等待。所以将CPU1对应的mcs_spinlock结构体加入单链表尾部。然后spin等待CPU1对应的mcs_spinlock结构体locked成员被置1。

    当CPU2继续申请锁时,发现链表不为空,说明有CPU在等待锁。所以也将CPU2对应的mcs_spinlock结构体加入链表尾部。

    当CPU0释放锁的时候,发现CPU0对应的mcs_spinlock结构体的next域不为NULL,说明有等待的CPU。然后将next域指向的mcs_spinlock结构体的locked成员置1,通知下个获得锁的CPU退出自旋。MCS lock头指针可以选择不更新,等到CPU2释放锁时更新为NULL。

    通过以上步骤,我们可以看到每个CPU都spin在自己的使用变量上面。

  • 相关阅读:
    Rust结构体和枚举
    C Primer Plus(6) 中文版 第11章 字符串和字符串函数 11.9 把字符串转换为数字 11.10 关键概念 11.11 本章小结
    SpringBoot中Bean的条件装配
    车轮上的智能:探索机器学习在汽车行业的应用前景
    Linux安装RabbitMQ教程(文件下载地址+安装命令+ 端口开放 + 用户创建 +配置文件模板+端口修改)
    基于Springboot的超市管理系统毕业设计-附源码231443
    场景应用:自己设计一个本地缓存(代码实现)
    一文搞懂│什么是跨域?如何解决跨域?
    第2-4-4章 规则引擎Drools规则属性-业务规则管理系统-组件化-中台
    Springboot项目RestController中函数参数注解使用
  • 原文地址:https://blog.csdn.net/qq_52353238/article/details/133013768