linux 驱动编程___竞态/并发问题___除了锁之外的其他方法

 

~  除了锁之外的办法

  • 循环缓冲区
    • 只要保持写入指针何读取指针不重叠, 就不会有问题
    • 典型实现: 网络适配器
    • 内核实现: <linux/kfifo.h>
  • 原子变量
    • 对于简单的共享资源使用
    • 代码实现
      • <asm/atomic.h>
      • 类型: atomic_t, 实质上是一个 int, 但不能大于24位
    • 初始化
      • void atomic_set(atomic_t *v, int i);// 设置v为整数值 i
      • stomic_t v=ATOMIC_INIT(0);//宏初始化
    • 应用函数
      • 读取值
        • int atomic_read(atomic_t *v);//返回v
      • 运算操作
        • void atomic_add(int i, atomic_t *v);//将 i 累加到 v 上
        • void atomic_sub(int i, atomic_t *v);//从 v 中减去 i
        • void atomic_inc(atomic_t *v);//增加 1
        • void atomic_dec(atomic_t *v);//减去 1
      • 运算并测试
        • 先运算再测试, 如果运算后为 0, 返回 ture, 否则返回fulse
          • int atomic_inc_and_test(atomic_t *v);
          • int atomic_dec_and_test(atomic_t *v);
          • int atomic_sub_and_test(int i, atomic_t *v);
        • 先运算, 结果为负返回ture, 否则返回 false
          • int atomic_add_negative(int i, atomic_t *v);
      • 有返回值的运算
        • int atomic_add_return(int i, atomic_t *v);//将 i 累加到 v 上
        • int atomic_sub_return(int i, atomic_t *v);//从 v 中减去 i
        • int atomic_inc_return(atomic_t *v);//增加 1
        • int atomic_dec_return(atomic_t *v);//减去 1
      • 注意事项
        • 不能将 atomic_t 数据传递给整型
        • atomic_t的数目是原子的才能正常工作, 多个 atomic_t 同时使用, 仍然需要锁
  • 位操作
    • 可原子的修改和测试单个位的函数
    • 代码实现
      • <asm/bitops.h>
    • 可操作函数
      • 设置 addr 指向的数据项的第 nr 位
        • void set_bit(nr, void *addr);
      • 清楚 addr 指向的数据项的第 nr 位
        • void clear_bit(nr, void *addr);
      • 切换指定位
        • void change_bit(nr, void *addr);
      • 返回指定位的当前值, 不必以原子方式实现
        • _test_bit(nr, void *addr);
      • 对位进行相应操作, 同时返回操作前的值
        • int test_and_set_bit(nr, void *addr);
        • int test_and clear_bit(nr, void *addr);
        • int test_and_change_bit(nr, void *addr);
  • seqlock
    • 使用情况
      • 当要保护的资源很小很简单, 会频繁被访问, 很少写入且必须快速时, 可以使用 seqlock.

        读取者可对资源自由访问, 但读取者需要检查是否和写入者冲突, 当发生时就需要重试对资源的访问

        seqlock通常不能保护包含指针的数据结构, 因当写入者修改数据结构时, 读取者可能会追随一个无效指针

    • 代码实现
      • <linux/seqlock.h>
      • 结构类型: _seqlock_t
    • 初始化
      • seqlock_t lock1 = SEQLOCK_UNLOCKED;//静态

        seqlock_t lock2;

        seqlock_init(&lock2);//动态:

    • seqlock 使用例程
      • unsigned int seq;

        do(

            seq=read_seqbegin(&the_lock);

            /*完成工作*/

        }while read_seqretry(&the_lock, seq);

    • 相关 API
      • 在中断处理中使用的 IRQ 安全版本
        • unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);
        • int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned long flags);
      • 写入者进入临界区时需要获得互斥锁
        • void write_seqlock(seqlock_t *lock);
        • void write_sequnlock(seqlock_t *lock);
        • 写入锁源自自旋锁, 因此其限制也等同于自旋锁
      • 来自自旋锁的变种函数
        • void write_seqlock_irqsave(seqlock_t *lock, unsigned long flags);
        • void write_seqlock_irq(seqlock_t *lock);
        • void write_seqlock_bh(seqlock_t *lock);
        • void write_sequnlock_irqsave(seqlock_t *lock, unsigned long flags);
        • void write_sequnlock_irq(seqlock_t *lock);
        • void write_sequnlock_bh(seqlock_t *lock);
        • int write_trylock(seqlock_t *lock);//成功时返回非零值
  • 读取-复制-更新
    • 简单说明
      • 简称 RCU ( read-copy-update)
      • 较少用在驱动程序上
      • 被保护资源需要由指针访问, 对这些资源的引用必须仅由原子代码拥有
      • 修改数据流程
        • 写入进程先复制
        • 修改副本
        • 用新的版本替代相关指针
        • 当确信老板本上没有其他引用了, 就可以释放老板本
    • 代码实现
      • <linux/rcupdate.h>

        rcu_read_lock

        rcu_read_unlock

    • 程序实现举例
      • struct my_stuff *stuff;

         

        rcu_read_lock();    //调用时会禁止内核抢占, 不会等待

        stuff-find_the_stuff(args...);

        do_something_with(stuff);

        rcu_read_unlock();

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值