Day21 atomic, memory barrier

前言

昨天讲完了最後一天的记忆体管理方法,了解了如何管理匿名分页 anonymous page,也知道了RMAP为何要存在,以及存在RMAP的好处。 只是前面讲的都是以单一行程的角度来看记忆体管理,当这些资料有非常多行程需要同时管理与同时使用呢? 如果有多个行程对某个资料同时存取,该如何保证资料的正确性呢? 这个时候就需要同步管理的机制,让行程可以依序拿到正确的资料。

atomic

Atomic Operation 若翻译为「原子操作」,同样会因汉语的语意而让我们理解受限,可改称为「最小操作」,即某个动作执行时,中间没有办法分割。

在开始描述最小操作前,我们先看看一个例子。

static int i = 0;

void thread_A(){
    i++;
}

void thread_B(){
    i++;
}

上述程序码的结果可能为1也可能为2,端看两个程序的执行顺序,理由是因为 i++ 的过程,可以拆解成三个,读取i -> i++ -> 存回i , 存在一种状况,如果两个thread 同时取得 i=0 的状态,则两个function的存回值都会是 i=1, 因此原子操作最基本的想法,就是把 "读取-> 修改 ->写入" 这个流程整合成一块,必须要三个步骤都完成,才能够让资料开放给其他人读取,这麽能保持资料的完整性。
在Linux kernel 内有 atomic_t 的结构维护最小操作

<include/linux/type.h>

type def struct{
    int counter;
}atomic_t;

以下说说几种 Linux内提供的基本最小操作函数

1. 基本的最小操作函数

<include/asm-generic/atomic.h>

void ATOMIC_INIT(i)                  //定义某个变量为 atomic_t
void atomic_set(atomic_t *v, int i); //设置v值为i
int atomic_read(atomic_t *v);        //读取v得值

atomic_set() , atomic_read() 这两个函数可以利用 READ_ONCE()WRITE_ONCE() 达成。只是让我们看看 READ_ONCE() WRITE_ONCE()的注释

Prevent the compiler from merging or refetching reads or writes. The compiler is also forbidden from reordering successive instances of READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some particular ordering. One way to make the compiler aware of ordering is to put the two invocations of READ_ONCE or WRITE_ONCE in different C statements.

可以发现,这样的最小操作函数只能应用在单核,因为 READ_ONCE() WRITE_ONCE() 这两个函数只能禁止编译器进行重排指令的动作,所以这边的指令在多核就会失去效果。

2. 没有返回值的最小操作

static inline void atomic_add(int i, atomic_t *v) 
static inline void atomic_sub(int i, atomic_t *v)
#define atomic_inc(v)			atomic_add(1, (v))
#define atomic_dec(v)			atomic_sub(1, (v))
atomic_{and, or, xor}(int i, atomic_t *v))


#define ATOMIC_OP(op, c_op)						\
static inline void atomic_##op(int i, atomic_t *v)			\
{									\
	int c, old;							\
									\
	c = v->counter;							\
	while ((old = cmpxchg(&v->counter, c, c c_op i)) != c)		\
		c = old;						\
}

上述的函数就可以解决多处理器同时存取同一个资料所引发的问题了。

3. 有返回值的原子操作函数

inline function补充

The point of making a function inline is to hint to the compiler that it is worth making some form of extra effort to call the function faster than it would otherwise - generally by substituting the code of the function into its caller. As well as eliminating the need for a call and return sequence, it might allow the compiler to perform certain optimizations between the bodies of both functions.

间单来说就是因为跳到呼叫的函数的时间,比函数本身的执行时间还要长,所以直接将那部份的程序码再编译时期就镶嵌进原本的程序码中。

4. 记忆体屏障 (memory barrier)

记忆体屏障(英语:Memory barrier),也称记忆体栅栏,记忆体栅障,屏障指令等,是一类同步屏障指令,它使得 CPU 或编译器在对记忆体进行操作的时候, 严格按照一定的顺序来执行, 也就是说在memory barrier 之前的指令和memory barrier之後的指令不会由於系统最佳化等原因而导致乱序。
大多数现代电脑为了提高效能而采取乱序执行,这使得记忆体屏障成为必须。
语意上,记忆体屏障之前的所有写操作都要写入记忆体;记忆体屏障之後的读操作都可以获得同步屏障之前的写操作的结果。因此,对於敏感的程序块,写操作之後、读操作之前可以插入记忆体屏障。

以下是几个Linux kernel中的记忆体屏障函数

barrier()                       //编译优化屏障,阻止编译器为了性能优化而进行指令重排 
mb()                            //记忆体屏障,包含读跟写
rmb()                           //读记忆体屏障
wmb()                           //写记忆体屏障
smp_mb()                        //用於smp的记忆体屏障
smp_rmb()                       //用於SMP的读取记忆体屏障
smp_wmb()                       //用於SMP的写入记忆体屏障


/* The "volatile" is due to gcc bugs */
#define barrier() asm volatile("": : :"memory")
/*volatile: 告诉编译器 barrier()周围的指令不要优化
memory:告诉编译器组合语言会使记忆体的值更改,编译器该使用记忆体内的新值而不是使用暂存器内部的旧值。
*/

##补充1
##补充2

Token concatenation
The ## operator (known as the "Token Pasting Operator") concatenates two tokens into one token.

Example:

#define DECLARE_STRUCT_TYPE(name) typedef struct name##_s name##_t
DECLARE_STRUCT_TYPE(g_object); // Outputs: typedef struct g_object_s g_object_t;

并行程序设计: Atomics 操作


<<:  [Day 21] Crypto 小烧声

>>:  Day21-大量图片的页面(上)_利用grid快速排版

set<E> 选出所有 E 第一个元素 java

有一SET物件 E 的内容有含 playerId,site,gamedate 有一段SQL sql....

D-29. 常数, 变数, 符号, 数字 && Leetcode : Power_of

资料型态 多数程序语言的学习第一步常为了解资料型态,毕竟分不清楚资料的型态的话,就不可能对资料做出正...

Elastic Stack第二十六重

Query DSL Part VII (查询语法) 本篇继续介绍 Query DSL 的 term-...

[Day 5] 就决定是你了!艺文资讯整合平台

我最後选择了什麽主题 我後来用了第三种-Open API的方式 因为这样就不用自己想资料内容了~ 这...

Day 27 介绍 gulp

插播一下, webpack 5 出来了 gulp 是个老牌的 task runner ,它就只是执...