主页
文章
分类
系列
标签
简历
【同步机制】原子操作
发布于: 2022-3-3   更新于: 2022-3-3   收录于: Linux Kernel
文章字数: 115   阅读时间: 1 分钟   阅读量:

原子操作是一种内核同步手段,所谓原子即操作不可分割,要么成功,要么失败

单独的读/写操作是原子性的

在单核系统中,如果CPU仅仅是从内存中读取(read/load)一个变量的值,或者仅仅是往内存中写入(write/store)一个变量的值,都是不可打断,也不可分割的。

在多核系统中,多个CPU通过共享总线和内存相连,读写每次读写操作都需要总线仲裁,确定哪一个CPU可以使用总线,然后获得授权的CPU会执行依次原子的load或store操作。所以在多核系统中,单独的读写操作也是原子性的。

1
2
#define atomic_read(v) READ_ONCE((v)->counter) //原子读
#define atomic_set(v,i) WRITE_ONCE(((v)->counter), (i))//原子写
1
2
READ_ONCE(p) --> *(__u32 *)res = *(volatile __u32 *)p;
WRITE_ONCE(p, val) --> *(volatile __u32 *)p = *(__u32 *)val;

修改一个变量值(RMW)

修改一个变量的步骤:1. 将变量值从内存中读取到寄存器 ; 2. 修改寄存器的值 ; 3. 将修改后的值写回该变量所在的内存位置

如何保证RMW操作的原子性呢

单核系统的实现——关中断

1
2
3
4
5
6
7
void atomic_add(int i, atomic_t *v)
{
	unsigned long flags;
	raw_local_irq_save(flags);
	v->counter += i;
	raw_local_irq_restore(flags);
}

多核系统的实现 : 锁总线 (X86)

x86处理器常用的做法是给总线上锁(bus lock),以获得在一定的时间窗口内对总线独占的授权,就好像是一个CPU在告诉总线说“在我完成之前,别让其他CPU来读写内存的数据”。

硬件中,在执行时会被硬件自动的或者隐式的加上LOCK#信号,在软件中,可以在部分命令前面加上lock,其实在现代体系结构中,因为Cache的引入,锁总线其实大部分情况下是锁住对应的Cache Line,只要保证了所访问地址的CacheLine的独占权,也就实现了操作的原子性。

但是注意,如果访问的数据,是跨越2个cache line (没有对齐的情况下造成的跨越两个Cache Line )就只能是Bus Lock , 所以非内存对齐对性能的影响是非常大的。

1
2
3
4
5
6
static __always_inline void arch_atomic_add(int i, atomic_t *v)
{
	asm volatile(LOCK_PREFIX "addl %1,%0"
	: "+m" (v->counter) // input+output
	: "ir" (i)); // input
}

[1] 读写一气呵成 - Linux中的原子操作