11

posix標準は、ミューテックスのようなものがメモリ同期を強制すると言っています。ただし、コンパイラはメモリ アクセスの順序を変更する場合があります。私たちが持っていると言う

lock(mutex);
setdata(0);
ready = 1;
unlock(mutex);

コンパイラの並べ替えで以下のコードに変更される可能性がありますよね?

ready = 1;
lock(mutex);
setdata(0);
unlock(mutex);

では、ミューテックスはどのようにしてメモリアクセスを同期できるのでしょうか? より正確に言えば、コンパイラは、ロック/ロック解除をまたいで並べ替えを行うべきではないことをどのように認識していますか?

ここでは実際にシングル スレッドの側面について説明します。ready は関数呼び出し lock(mutex) で使用されないため、ready 割り当ての並べ替えは完全に安全です。

EDITED:関数呼び出しがコンパイラが通過できないものである場合、それをコンパイラのメモリバリアと見なすことができます

asm volatile("" ::: "memory")
4

2 に答える 2

8

一般的な答えは、コンパイラを POSIX ターゲットに使用する場合、コンパイラは POSIX をサポートする必要があるということです。そのサポートは、ロックとロック解除の間で並べ替えを回避する必要があることを意味します。

とはいえ、この種の知識は一般的に些細な方法で達成されます。コンパイラは、それらを使用または変更する可能性のある外部関数への呼び出し全体で、(証明可能ではない) データへのアクセスを並べ替えません。何か特別なことlockを知っていunlockて、再注文できるはずです。

いいえ、「グローバル関数の呼び出しは常にコンパイラの障壁である」ほど単純ではありません。「コンパイラがその関数について特定のことを認識していない限り」を追加する必要があります。これは実際に起こります。たとえばpthread_self、Linux (NPTL) では、__const__属性を指定して宣言されているため、呼び出しgcc間で並べ替えが可能になりpthread_self()、不要な呼び出しを完全に排除することさえできます。

取得/解放セマンティクスの関数属性をサポートするコンパイラーを容易に想像できます。完全なコンパイラーの障壁にはlockなりunlockません。

于 2013-01-21T11:53:48.197 に答える
3

コンパイラは、安全であることが明らかでない場合、順序を変更しません。あなたの「もしも」の例では、並べ替えられたメモリアクセスを提案しているのではなく、コンパイラがコードの順序を完全に変更した場合にどうなるかを尋ねていますが、そうはなりません。コンパイラが行う可能性があるのは、実際のメモリの読み取り/書き込みの順序を変更することですが、関数呼び出しは変更しません (これらのメモリ アクセスに関係なく)。

コンパイラがメモリアクセスの順序を変更する可能性のある場所の例...次のコードがあるとしましょう:

a = *pAddressA;
b = *pAddressB;

pAddressBの値がレジスタにあり、レジスタにない場合を考えてみましょうpAddressA。コンパイラが最初にアドレス B を読み取り、次に の値pAddressAを同じレジスタに移動して、新しい位置を受信できるようにするのは公正なゲームです。これらのアクセスの間にたまたま関数呼び出しがあった場合、コンパイラはこれを行うことができません。

于 2013-01-21T11:51:37.483 に答える