并发与竞态——除了锁之外的办法

Release log:

2021-05-30 日: 完成初版

原文地址

在某些情况下,原子的访问可以不需要完整的锁

免锁算法

经常用于免锁的生产者/消费者任务的数据结构之一是循环缓冲区。内核中有一个通用的循环缓冲区实现,参阅<linux/kfifo.h>

原子变量

如果共享的资源是一个简单的整数值,则可以使用原子变量 atomic_t

一个 atomic_t 变量可以用来保存一个 int 值,但它不能记录大于 24 位的整数

对应的函数

// 初始化原子变量
void atomic_set(atomic_t *v, int i);
atomic_t v = ATOMIC_INIT(0);

int atomic_read(const atomic_t *v);
void atomic_add(int i, atomic_t *v); // 返回值是 void,是因为返回新的值将带来额外的成本
void atomic_sub(int i, atomic_t *v);
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);

// 会测试操作后的结果,如果原子值为 0,则返回值为 true,否则为 false
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);

// 将整数变量 i 累加到 v。返回值在结果为负时为 true,否则为 false
int atomic_add_negative(int i, atomic_t *v);

// 会将新的值返回给调用者
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);

位操作

对于需要以原子形式来操作单个位的情况,内核提供了一组可原子地修改和测试单个位的函数。使用这些函数,需要包含头文件 <asm/bitops.h>

这些函数整理如下(nr 的类型可能依赖于具体架构):

void set_bit(int nr, volatile unsigned long *addr);
void clear_bit(int nr, volatile unsigned long *addr);
void change_bit(int nr, volatile unsigned long *addr);
test_bit(int nr, volatile unsigned long *addr);

// 会返回这个位的先前值
int test_and_set_bit(int nr, volatile unsigned long *addr);
int test_and_clear_bit(int nr, volatile unsigned long *addr);
int test_and_change_bit(int nr, volatile unsigned long *addr);

seqlock

seqlock 可提供对共享资源的快速、免锁访问。当要保护的资源很小、很简单、会频繁被访问而且写入访问很少发生且必须快速时,就可以使用 seqlock。seqlock 通常不能用于保护包含有指针的数据结构,因为在写入者修改该数据结构的同时,读取者可能会追随一个无效的指针

seqlock 在 <linux/seqlock.h> 中定义,seqlock 的定义以及初始化如下:

typedef struct {
	struct seqcount seqcount;
	spinlock_t lock;
} seqlock_t;

seqlock_t lock;
seqlock_init(&lock);

读取访问通过获得一个整数顺序值而进入临界区。在退出时,该顺序值会和当前值比较;如果不相等,则必须重试读取访问。读取者如下编写

do {
	seq = read_seqbegin(&the_lock);
	/* 完成需要的工作 */
} while (read_seqretry(&the_lock, seq));

如果在中断中使用,需要使用 IRQ 版本(3.18.6 内核版本中,没有找到这两个函数)

unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);
int read_reqretry_irqrestore(seqlock *lock, unsigned int seq, unsigned long flags);

写入者进入临界区时,需要获取一个互斥锁。写入锁使用自旋锁实现,因此自旋锁的常见限制也适用于写入锁

void write_seqlock(seqlock_t *sl);
void write_seqlock_irqsave(reqlock_t *lock, unsigned long flags);
void write_seqlock_irq(seqlock_t *sl);
void write_seqlock_bh(seqlock_t *sl);

void write_unlock(seqlock_t *sl);
void write_unlock_irqrestore(seqlock_t *sl, unsigned long flags);
void write_unlock_irq(seqlock_t *sl);
void write_unlock_bh(seqlock_t *sl);

读取-复制-更新

读取-复制-更新(read-copy-update, RCU)也是一种高级的互斥机制,在正确的条件下,也可获得高性能

RCU 机制相关的几个博文

https://www.cnblogs.com/qcloud1001/p/7755331.html
https://blog.csdn.net/xabc3000/article/details/15335131

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值