EN JA
ATOMIC(9)
ATOMIC(9) FreeBSD Kernel Developer's Manual ATOMIC(9)

名称

atomic_add, atomic_clear, atomic_cmpset, atomic_fetchadd, atomic_load, atomic_readandclear, atomic_set, atomic_subtract, atomic_store不可分操作

書式

#include < sys/types.h>
#include < machine/atomic.h>

void
atomic_add_[acq_|rel_]<type>( volatile <type> *p, <type> v);

void
atomic_clear_[acq_|rel_]<type>( volatile <type> *p, <type> v);

int
atomic_cmpset_[acq_|rel_]<type>( volatile <type> *dst, <type> old, <type> new);

<type>
atomic_fetchadd_<type>( volatile <type> *p, <type> v);

<type>
atomic_load_acq_<type>( volatile <type> *p);

<type>
atomic_readandclear_<type>( volatile <type> *p);

void
atomic_set_[acq_|rel_]<type>( volatile <type> *p, <type> v);

void
atomic_subtract_[acq_|rel_]<type>( volatile <type> *p, <type> v);

void
atomic_store_rel_<type>( volatile <type> *p, <type> v);

<type>
atomic_swap_<type>( volatile <type> *p, <type> v);

int
atomic_testandset_<type>( volatile <type> *p, u_int v);

解説

それぞれの不可分の操作は、割り込みがあるとき不可分であることが保証されます。それらは、参照カウントまたはミューテックスのような、より進歩した同期プリミティブ (基本関数) のためのブロックを構築するような実装のために使用することができます。

タイプ

それぞれの不可分の操作は、特定の type を操作します。使用するタイプは、関数名で示されます。使用することができる利用可能なタイプは、次の通りです:

int
unsigned integer (符号なし整数)
long
unsigned long integer (符号なし long 整数)
ptr
unsigned integer the size of a pointer (ポインタのサイズの符号なし整数)
32
unsigned 32-bit integer (符号なし 32 ビット整数)
64
unsigned 64-bit integer (符号なし 32 ビット整数)

例えば、不可分に 2 つの整数を加える関数は、 atomic_add_int() と呼ばれます。

また、特定のアーキテクチャは、“ int”より小さいタイプのための操作も提供します。

char
unsigned character (符号なし文字)
short
unsigned short integer (符号なし short 整数)
8
unsigned 8-bit integer (符号なし 8 ビット整数)
16
unsigned 16-bit integer (符号なし 16 ビット整数)

それらを実装する指示が効率的に利用可能でないかもしれないので、これらは、MI コードで使用されてはいけません。

メモリバリア

メモリバリアは、2 つの方法でデータアクセスの順序を保証するために使用されます。最初に、それらは、操作を再順序付けしないか、または最適化しないようにコンパイラにヒントを指定します。 2 番目に、順序付けられたデータアクセスを保証しないアーキテクチャでは、指示の特有の指示か等有の異形は、データアクセスが特定の順序で起こる必要であることをプロセッサに示すために使用されます。結果として、不可分の操作の大部分には、オプションのメモリバリアを含むための 3 つの異形があります。最初の形式は、少しの明白なバリアなしで操作をただ実行します。 2 番目の形式は、読み込みメモリバリアを使用し、 3 番目の異形は、書き込みメモリバリアを使用します。

それぞれの操作の 2 番目の異形は、読み込みメモリバリアを含んでいます。このバリアは、この操作の効果がどんな後のデータアクセスの効果の前で終了していることを確実にします。結果として、操作は、完了されない場合、ウェートするさらなる操作を必要とする疑似ロックを獲得するように、セマンティクスを獲得していると言われています。これを示すために、接尾辞“ _acq”が“ _< type>”接尾辞のすぐ前の関数名に挿入されます。例えば、2 つの整数を引き算するために、 atomic_subtract_acq_int() を使用して、引き算が実行された後に任意の後の書き込みが起こることを確実にします。

それぞれの操作の 3 番目の異形は、書き込みメモリバリアを含みます。これは、すべての前のデータアクセスのすべての効果がこの操作が行われる前に終了していることを確実にします。結果として、操作は、操作が実行される前に完了されるためにあらゆる保留中 (pending) のデータアクセスを解放するように解放セマンティクスを持っていると言われています。これを示すために、接尾辞“ _rel”が“ _< type>”接尾辞のすぐ前の関数名に挿入されます。例えば、2 つの long 整数を足すために、 atomic_add_rel_long() を使用して、すべて前の書き込みが最初に起こることを確実にします。

メモリバリアを使用する実例は、ロックによって保護されるデータアクセスがロックが保持されている間にすべて実行されることを確実にすることです。これを達成するために、任意の保護された操作が実行される前に、ロックが保持されることを保証するためにロックを獲得するとき、読み込みバリアを使用するでしょう。最後に、保護された操作のすべてがロックが解放される前に完了することを確実にするためにロックを解放するとき、書き込みバリアを使用するでしょう。

複数のプロセッサ

不可分な操作の現在の組は、複数のプロセッサにわたって不可分を保証する必要はありません。プロセッサにわたって不可分を保証するために、操作を実行するプロセッサで不可分にする必要がある無効の操作のみを行いませんが、操作の結果は、安定した記憶域に押し出される必要があり、システム上の他のすべてのプロセッサのキャッシュは、影響を受けるメモリ領域を含んでいるどんなキャッシュラインも無効にする必要があります。 i386 アーキテクチャでは、キャッシュ一貫性モデルは、ハードウェアがこのタスクを実行することを要求します、その結果、不可分な操作は、複数のプロセッサにわたって不可分です。 ia64 アーキテクチャでは、キャッシュしないか、または書き戻すか、いずれかのキャッシングポリシを使用して設定されるページのみ一貫性が保証されます。

動作 (セマンティクス)

このセクションは、C のような記法を使用してそれぞれの操作の動作について説明しています。
atomic_add( p, v)
*p += v;
atomic_clear( p, v)
*p &= ~v;
atomic_cmpset( dst, old, new)
if (*dst == old) { 
 *dst = new; 
 return (1); 
} else 
 return (0);

atomic_cmpset() 関数は、タイプ“ char”, “ short”, “ 8”と“ 16”の実装を行いません。

atomic_fetchadd( p, v)
tmp = *p; 
*p += v; 
return (tmp);

atomic_fetchadd() 関数は、タイプ“ int”, “ long”と“ 32”のためだけに実装され、現時点では、メモリバリアがあるどんな異形もありません。

atomic_load( p)
return (*p);

atomic_load() 関数は、メモリバリアの獲得のためだけに提供されています。

atomic_readandclear( p)
tmp = *p; 
*p = 0; 
return (tmp);

atomic_readandclear() 関数は、タイプ“ char”, “ short”, “ ptr”, “ 8”と“ 16”の実装を行いません、そして今のところメモリバリアがあるどんな異形もありません。

atomic_set( p, v)
*p |= v;
atomic_subtract( p, v)
*p -= v;
atomic_store( p, v)
*p = v;

atomic_store() 関数は、メモリバリアの解放のためだけに提供されています。

atomic_swap( p, v)
tmp = *p; 
*p = v; 
return (tmp);

atomic_swap() 関数は、タイプ“ char”, “ short”, “ ptr”, “ 8”と“ 16”のために実装されず、現時点で、メモリバリアがある、あらゆる変異型はありません。

atomic_testandset( p, v)
bit = 1 << (v % (sizeof(*p) * NBBY)); 
tmp = (*p & bit) != 0; 
*p |= bit; 
return (tmp);

atomic_testandset() 関数は、タイプ“ int”, “ long”と“ 32”のためだけに実装され、現時点で、メモリバリアがある、あらゆる変異型はありません。

タイプ“ 64”は、現在 arm, i386 と powerpc アーキテクチャでの不可分操作としてなにも実装されていません。

戻り値

atomic_cmpset() 関数は、比較操作の結果を返します。 atomic_fetchadd(), atomic_load(), atomic_readandclear() と atomic_swap() 関数は、指定されたアドレスの値を返します。 atomic_testandset() 関数は、テスト操作の結果を返します。

使用例

この例は、スリープミューテックスを取得して、再帰を取り扱うために atomic_cmpset_acq_ptr() と atomic_set_ptr() 関数を使用します。 struct mtxmtx_lock メンバがポインタであるので、“ ptr”タイプが使用されます。

/* 一度 mtx_lock 取得しようと試みます. */ 
#define _obtain_lock(mp, tid)      \ 
 atomic_cmpset_acq_ptr(&(mp)->mtx_lock, MTX_UNOWNED, (tid)) 
 
/* スリープロックを取得し、再帰インラインを処理します. */ 
#define _get_sleep_lock(mp, tid, opts, file, line) do {   \ 
 uintptr_t _tid = (uintptr_t)(tid);    \ 
         \ 
 if (!_obtain_lock(mp, tid)) {     \ 
  if (((mp)->mtx_lock & MTX_FLAGMASK) != _tid)  \ 
   _mtx_lock_sleep((mp), _tid, (opts), (file), (line));\ 
  else {       \ 
   atomic_set_ptr(&(mp)->mtx_lock, MTX_RECURSE); \ 
   (mp)->mtx_recurse++;    \ 
  }       \ 
 }        \ 
} while (0)

歴史

atomic_add(), atomic_clear(), atomic_set() と atomic_subtract() 操作は、 FreeBSD 3.0 ではじめて登場しました。この最初のセットは、タイプ“ char”, “ short”, “ int”と“ long”のみをサポートしていました。 atomic_cmpset(), atomic_load(), atomic_readandclear() と atomic_store() 操作は、 FreeBSD 5.0 で追加されました。タイプ“ 8”, “ 16”, “ 32”, “ 64”と“ ptr”と獲得され解放されるバイアントのすべては、同様に FreeBSD 5.0 で追加されました。 atomic_fetchadd() 操作は、 FreeBSD 6.0 で追加されました。 atomic_swap() と atomic_testandset() 操作は、 FreeBSD 10.0 で追加されました。
August 20, 2013 FreeBSD