linux 驱动编程___竞态/并发问题___自旋锁

~  自旋锁

  • 自旋锁的几点说明
    • 与信号量不同, 自旋锁可在不能休眠的代阿中使用, 比如中断处理例程
    • 一个自旋锁就是一个互斥设备, 只有两个状态: 锁定 和 解锁
    • 自旋锁是某个整数值的单个位, 希望获得锁的进程需测试相关位
    • 锁可用, 则"锁定"位被设置, 进程进入临界区
    • 锁不可用, 则进程进入忙循环, 重复检查该锁, 直到可用为止
    • 测试和设置 的操作必须以原子方式进行
  • 自旋锁 API 介绍
    • 结构实现
      • <linux/spinlock.h>
      • 锁的类型: spinlock_t
    • 初始化
      • 静态初始化
        • spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
      • 动态初始化
        • void spin_lock_init(spinlock_t *lock);
    • 操作
      • 说明
        • 所有自旋锁等待在本质上都是不可中断的, 一旦调用spin_lock, 在获得锁之前一直处于自旋
      • 获取锁操作
        • void spin_lock(spinlock_t *lock);
      • 释放锁操作
        • void spin_unlock(spinlock_t *lock);
  • 使用锁的基本规则
    • 拥有自旋锁的代码是原子的, 不能休眠
      • 不能因为任何原因放弃处理器
      • 中断除外
      • 在编写自旋锁下执行的代码时, 要注意任何一个函数, 保证不会休眠
    • 在拥有自旋锁时需要禁止中断(仅在本地CPU)
    • 自旋锁需要在尽可能短的时间内拥有
  • 自旋锁函数
    • 锁定自旋锁函数
      • void spin_lock(spinlock_t *lock);

        • 获得自旋锁

        void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);

        • 在获得自旋锁前禁止中断(在本地CPU), 中断状态保存在 flags中

        void spin_lock_irq(spinlock_t *lock);

        • 在释放自旋锁时启用中断, 不需要跟踪标志

        void spin_lock_bh(spinlock_t *lock);

        • 在获得锁之前禁止软中断, 保持硬中断打开

        如果自旋锁可以被中断(包括软/硬中断)中的代码获得, 我们需要禁止中断;如果锁不会在硬中断中获得, 却可能在软中断中获得, 那么应该使用 spin_lock_bh, 这样即使锁死, 也可以响应硬中断.

    • 释放自旋锁函数
      • void spin_unlock(spinlock_t *lock);
      • void spin_unlock_irqsave(spinlock_t *lock, unsigned long flags);
        • 需要与 spin_lock_irqsave 在同一函数中调用
      • void spin_unlock_irq(spinlock_t *lock);
      • void spin_unlock_bh(spinlock_t *lock);
    • 非阻塞的自旋锁操作
      • 说明: 这俩个函数在成功时返回非零值, 否则返回零值
      • int spin_trylock(spinlock_t *lock);
      • int spin_trylock_bh(spinlock_t *lock);
  • 读取者/写入者自旋
    • 介绍及代码实现
      • 类似于 读取写入信号量, 可以允许多个读取者, 只有一个写入者
      • <linux/spinlock.h>
      • 类型: rwlock_t
    • 声明和初始化
      • 静态方式: rwlock_t my_rwlock = FW_LOCK_UNLOCKED;

        动态方式: 

        rwlock_t my_rwlock;

        rwlock_init(&my_rwlock);

    • 读取者函数
      • 获得锁
        • void read_lock(rwlock_t *lock);
        • void read_lock_irqsave(rwlock_t *lock,unsigned long flags);
        • void read_lock_irq(rwlock_t *lock);
        • void read_lock_bh(rwlock_t *lock);
      • 释放锁
        • void read_unlock(rwlock_t *lock);
        • void read_unlock_irqsave(rwlock_t *lock,unsigned long flags);
        • void read_unlock_irq(rwlock_t *lock);
        • void read_unlock_bh(rwlock_t *lock);
    • 写入者函数
      • 获得锁
        • void write_lock(rwlock_t *lock);
        • void write_lock_irqsave(rwlock_t *lock,unsigned long flags);
        • void write_lock_irq(rwlock_t *lock);
        • void write_lock_bh(rwlock_t *lock);
        • void write_trylock(rwlock_t *lock);
      • 释放锁
        • void write_unlock(rwlock_t *lock);
        • void write_unlock_irqsave(rwlock_t *lock,unsigned long flags);
        • void write_unlock_irq(rwlock_t *lock);
        • void write_unlock_bh(rwlock_t *lock);
  • 锁陷阱
    • 不明确规则
      • 控制访问的锁应该在可并发对象创建时就定义
      • 信号量和自旋锁都不允许二次获得锁
      • 内部函数可以(或者需要)假定已经获得锁, 但提供外部的函数必须显式处理锁
    • 锁的顺序调用
      • 当多个进程需要同时获得多个锁时, 进程需要按照相同的顺序获得锁
      • 在信号量和自旋锁组合使用时, 需要先获得信号量, 再获得自旋锁
  • 细粒度和粗粒度
    • 通常规则, 应该在最初使用粗粒度, 真正的性能约束一般出现在非预期情况下
    • 工具 lockmeter 可以度量锁花费的时间
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值