アトミック操作とは
アトミック操作は、複数のプロセスやスレッドから同時にアクセスされる可能性のある変数へのアクセスを同期化するために使用される方法の1つです。アトミック操作は、複数のCPUで同時に実行されている場合でも、互いに競合することなく実行されます。アトミック操作は、プロセスのスケジューリングや割り込み処理のような高頻度の操作で必要な場合があります。
使い方
atomic操作関数には、atomic_set
、atomic_read、atomic_add
、atomic_sub
等があります。atomic_set
はalsps_probeから呼ばれるalsps_context_alloc_objectから使用されています。
kernel/mediatek/4.4/drivers/misc/mediatek/sensors-1.0/alsps/alsps.c
atomic_set(&prox_state, PROX_STATE_FAR);
arm64において、atomic操作関数は、以下の通り定義されています。
kernel/mediatek/4.4/arch/arm64/include/asm/atomic.h
#define atomic_read(v) READ_ONCE((v)->counter)
#define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
kernel/mediatek/4.4/arch/arm64/include/asm/atomic_lse.h
static inline void atomic_add(int i, atomic_t *v)
static inline void atomic_sub(int i, atomic_t *v)
第1引数の値に対して、第2引数の値を、atomic_setであれば代入と、関数名通りに処理する動作となります。
最後に
atomic_setが置き換えられるWRITE_ONCE
は、以下の通り定義されています。
kernel/mediatek/4.4/include/linux/compiler.h
#define WRITE_ONCE(x, val) \
({ \
union { typeof(x) __val; char __c[1]; } __u = \
{ .__val = (__force typeof(x)) (val) }; \
__write_once_size(&(x), __u.__c, sizeof(x)); \
__u.__val; \
})
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
switch (size) {
case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
default:
barrier();
__builtin_memcpy((void *)p, (const void *)res, size);
barrier();
}
}
通常の変数においてはvolatile修飾子で定義したアドレスに値を代入しているだけです。この修飾子はコンパイラによって最適化されることを防ぐ為の物です。レジスタへのアクセス等も考慮すると、コンパイラでは分からない、必要なメモリ空間操作、順番の意味が発生します。例えば、1章 デバイスドライバとは 「ハードウェアを制御する仕組み」の図で示している例の、0x10000000番地への書き込みも、0x10000004番地の値が変わりますが、通常のRAMではそのような動きが無いため、コンパイラの最適化によって削除される場合があります。volatile修飾子はこれを防ぐことができ、組み込み開発においては多用される仕組みで、atomic_setもこれを使用しています。
コメント