「インラインアスミックを使用するのは悪い考えです」と言って始めましょう。また、「インラインasmを使用するのは悪い考えです」と繰り返してみましょう。あなたはインラインasmを使用することが悪い考えである理由について全体をwiki entryと書くことができます。組み込み関数(gccの__sync_bool_compare_and_swapなど)または<アトミック>などのライブラリを使用することを検討してください。
プロダクションソフトウェアを作成している場合、インラインasmを使用するリスクは、ほとんどのメリットよりも確かに大きいです。あなたが教育目的のために執筆しているなら、次に読んでください。
(インラインasmを使用しない理由の詳細については、マイケルまたはピーターがこのコードで間違っていることを指摘してください。本当に物事、それを右に得るために)
cmpxchg8b
を使用する方法を示すいくつかのコードです。それは簡単ですが、一般的な考え方を与えるには十分でなければなりません。
#include <stdio.h>
// Simple struct to break up the 8 byte value into 32bit chunks.
typedef union {
struct {
unsigned int lower;
unsigned int upper;
};
unsigned long long int f;
} moo;
unsigned char cas(moo *ptr, moo *oldval, const moo *newval)
{
unsigned char result;
#ifndef __GCC_ASM_FLAG_OUTPUTS__
asm ("lock cmpxchg8b %[ptr]\n\t"
"setz %[result]"
: [result] "=q" (result), [ptr] "+m" (*ptr),
"+d" (oldval->upper), "+a" (oldval->lower)
: "c" (newval->upper), "b" (newval->lower)
: "cc", "memory");
#else
asm ("lock cmpxchg8b %[ptr]"
: [result] "[email protected]" (result), [ptr] "+m" (*ptr),
"+d" (oldval->upper), "+a" (oldval->lower)
: "c" (newval->upper), "b" (newval->lower)
: "memory");
#endif
return result;
}
int main()
{
moo oldval, newval, curval;
unsigned char ret;
// Will not change 'curval' since 'oldval' doesn't match.
curval.f = -1;
oldval.f = 0;
newval.f = 1;
printf("If curval(%u:%u) == oldval(%u:%u) "
"then write newval(%u:%u)\n",
curval.upper, curval.lower,
oldval.upper, oldval.lower,
newval.upper, newval.lower);
ret = cas(&curval, &oldval, &newval);
if (ret)
printf("Replace succeeded: curval(%u:%u)\n",
curval.upper, curval.lower);
else
printf("Replace failed because curval(%u:%u) "
"needed to be (%u:%u) (which cas has placed in oldval).\n",
curval.upper, curval.lower,
oldval.upper, oldval.lower);
printf("\n");
// Now that 'curval' equals 'oldval', newval will get written.
curval.lower = 1234; curval.upper = 4321;
oldval.lower = 1234; oldval.upper = 4321;
newval.f = 1;
printf("If curval(%u:%u) == oldval(%u:%u) "
"then write newval(%u:%u)\n",
curval.upper, curval.lower,
oldval.upper, oldval.lower,
newval.upper, newval.lower);
ret = cas(&curval, &oldval, &newval);
if (ret)
printf("Replace succeeded: curval(%u:%u)\n",
curval.upper, curval.lower);
else
printf("Replace failed because curval(%u:%u) "
"needed to be (%u:%u) (which cas has placed in oldval).\n",
curval.upper, curval.lower,
oldval.upper, oldval.lower);
}
いくつかのポイント:(値が一致しないため)CASが失敗した場合
- 、関数の戻り値は0で、その値は、あなたが使用する必要ですoldvalで返されました。これは再び簡単にしようとする。あなたがマルチスレッドを実行している場合(つまり、
lock cmpxchg8b
を使用していない場合)、もう1つの試みが失敗することも考えられます。なぜなら、「他の」スレッドはあなたをもう一度書き込むことができたからです。
__GCC_ASM_FLAG_OUTPUTS__
の定義は、gcc(6.x +)の新しいビルドで利用できます。これにより、setz
をスキップしてフラグを直接使用することができます。詳細については、gcc docsを参照してください。それがどのように動作するかについては
:
我々はcmpxchg8b
を呼び出すと、我々はそれをメモリへのポインタを渡します。そのメモリ位置にある(8バイト)値をedx:eaxの8バイトと比較します。一致した場合、ecx:ebxの8バイトをメモリ位置に書き込み、zero
フラグがセットされます。一致しない場合、現在の値がedx:eaxに返され、zero
フラグがクリアされます。ここで
asm ("lock cmpxchg8b %[ptr]"
我々はcmpxchg8b
に8つのバイトへのポインタを渡している。
だから、コードとすることを比較します。
"setz %[result]"
ここでは、(結果)にcmpxchg8b
によって設定zero
フラグの内容を記憶しています。
: [result] "=q" (result), [ptr] "+m" (*ptr),
(結果)が出力(=)で、バイトレジスタ(q)であることを指定します。また、メモリポインタはin + out(+)になっています。読み書きと書き込みの両方になるからです。
+記号は、これらの値が再び+ outにあることを示します。比較が失敗した場合、edx:eaxはptrの現在の値で上書きされるため、これは必要です。
: "c" (newval->upper), "b"(newval->lower)
これらの値は入力専用です。 cmpxchg8b
は値を変更しないので、2番目のコロンの後に入れます。
: "cc", "memory");
フラグを変更しているので、コンパイラに「cc」で通知する必要があります。正確にどのcasが使用されているかに応じて、 "memory"制約は必要ないかもしれません。スレッド1が何らかの処理の準備ができていることをスレッド2に通知している可能性があります。その場合、後でメモリに書き込む予定のレジスタにgccに値がないことを絶対に確認したいとします。 cmpxchg8b
を実行する前に、すべてをメモリにフラッシュする必要があります。
gcc docsは、拡張asmステートメントの動作を詳しく説明しています。この説明の一部がまだ不明な部分がある場合は、いくつかの読者が役に立つかもしれません。私は言及を忘れてしまった場合、ところで
、インラインアセンブラを書くことは悪い考えです...
はhttp://stackoverflow.com/questions/6756985/correct-way-to-wrap-cmpxchg8b-in-をいgcc-inline-assembly-32-bitは役に立ちますか? – user200783
cmpxchg8bのドキュメントを読んだことがありますか?「EDX:EAXとm64を比較してください。等しい場合は、ZFを設定し、ECX:EBXをm64にロードします。それ以外の場合は、ZFをクリアし、m64をEDX:EAXにロードします。EDX(またはEBX)に特定の値をロードしていないので、asmが何もしないことを意味するcompareが常に失敗し、 'prev'最適化されていないビルドでは)変更されません。さらに、cmpxchg8bに渡すメモリアドレスはptrではなく 'prev'(別名%0)なので、ptrは決して使用されません。 * ptr(vs ptr)はおそらく有効なメモリアドレスではないため、おそらく同様です。 –
また、あなたのプラットフォーム(x86と言っています)の 'unsigned long'はどれくらいですか?答えが8バイトでない場合は、cmpxchg8bを使用して再考する必要があります。なにが問題ですか?私はそれがほとんどすべてであることが怖いです。 –