linux驱动23:并发与静态(除了锁之外的办法)

内核循环缓冲区(环形缓冲区):

循环缓冲区是经常用于免锁的生产者/消费者任务的数据结构,适用于只有一个生产者和一个消费者的情况,如果是多个生产者或消费者时需要加锁。循环缓冲区在设备驱动程序中使用普遍。

算法:一个生产者将数据放入数组的结尾,而消费者从数组的另一端移走数据。在达到数组尾部的时候,生产者绕回到数组的头部。因此,一个循环缓冲区需要一个数组以及两个索引值,一个用于下一个要写入新值的位置,而另一个用于下一个从缓冲区中移走值的位置。

内核有一个通用的循环缓冲区实现,参阅<linux/kfifo.h>。

原子变量:

完整的锁机制对一个简单的整数来讲显得有些浪费,当共享的资源是一个简单的整数值时可以使用原子变量。内核提供了一种原子的整数类型,称为atomic_t,定义在<asm/atomic.h>,内核关于原子变量提供了一组相关函数接口,原子变量只能通过这些接口来访问。

当需要多个atomic_t变量的操作,仍然需要加锁。(两个连续的原子变量操作之间会有一小段时间,可能导致在这段时间运行的代码有问题)

位操作:

当需要以原子的形式来操作一个bit时,内核提供了一组可原子地修改和测试单个位的函数。这些函数依赖于具体的架构,在<asm/bitops.h>中声明。

seqlock:

当要保护的资源很小、很简单、会频繁访问而且写入访问很少发生且必须快速时,可以使用seqlock。seqlock会运行读取者对资源的自由访问,但需要读取者检查是否和写入者发生冲突,当发生冲突时,需要重试对资源的访问。seqlock通常不能用于保护包含指针的数据结构。seqlock在<linux/seqlock.h>中定义。

读取访问通过获得一个无符号的整数顺序值而进入临界区。在提出时该顺序值会和当前值比较,如果不相等,则需要重试读取访问。

seqlock通常用于保护某种类型 简单计算,这种计算需要多个一致的值。如果计算结束时发现已发生并发的修改,则可以简单丢弃结果并重新计算。

读取-复制-更新(read-copy-update,RCU):

RCU是一种高级的互斥机制,但很少在驱动程序中使用。相关接口声明在<linux/rcupdate.h>。

RCU对保护的数据结构做了一些限定。针对经常发生读取而很少写入的的情形做了优化。被保护的资源应该通过指针访问,而对资源的引用必须由原子代码拥有。

在修改数据结构时,写入线程首先复制,然后修改副本,之后用新的版本替代相关指针。当内核确信老版本的数据结构没有其他引用时就可以释放。

在读取时,代码使用受RCU保护的数据结构时,必须将引用数据结构的代码包含在rcu_read_lock和rcu_read_unlock调用之间。

修改受RCU保护的数据结构的代码必须分配一个struct rcu_head数据结构来获得清除用的回调函数,在修改完资源之后,应该调用:

void call_rcu(struct rcu_head *head, void (*func)(void *arg), void *arg);

在可安全释放资源时,给定的func会被调用,传递到call_rcu的参数arg会传给func函数。通常func要做的就是调用kfree。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值