- 循环缓冲区
- 只要保持写入指针何读取指针不重叠, 就不会有问题
- 典型实现: 网络适配器
- 内核实现: <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);
- 先运算再测试, 如果运算后为 0, 返回 ture, 否则返回fulse
- 有返回值的运算
- 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);
- 设置 addr 指向的数据项的第 nr 位
- 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);//成功时返回非零值
- 在中断处理中使用的 IRQ 安全版本
- 使用情况
- 读取-复制-更新
- 简单说明
- 简称 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();
-
- 简单说明