3

私はいくつかのコードを見直しており、使用されている手法に疑いを感じています。

Linux 環境では、複数の共有メモリ セグメントを接続する 2 つのプロセスがあります。最初のプロセスは、共有する新しいファイル セットを定期的にロードし、共有メモリ ID (shmid) を「マスター」共有メモリ セグメント内の場所に書き込みます。2 番目のプロセスは、この「マスター」の場所を継続的に読み取り、shmid を使用して他の共有セグメントを接続します。

マルチCPUホストでは、あるプロセスが他のプロセスによって書き込まれている間にメモリを読み取ろうとするとどうなるかは、実装に依存するようです。しかし、おそらくハードウェア レベルのバス ロックにより、ワイヤ上のビットの破損が防止されるでしょうか? 読み取りプロセスがすぐに変更される値を取得したかどうかは問題ではありません。読み取りが古い値でも新しい値でもない何かに破損した場合にのみ問題になります。これは特殊なケースです。32 ビットのみが読み書きされています。

シュマットのものをグーグルで検索しても、この分野で決定的なものにはなりませんでした。

私はそれが安全でも正気でもないことを強く疑っています.私が本当に欲しいのは、問題を詳細に説明する記事へのいくつかのポインタです.

4

13 に答える 13

12

それは合法です-OSのようにあなたがそれをするのを止めることはありません。

しかし、それは賢いですか?いいえ、ある種の同期が必要です。

「ワイヤー上の壊れたビット」はありません。それらは1または0として出てきます。ただし、別のプロセスがビットを読み取ろうとする前に、すべてのビットが書き出されることは言うまでもありません。また、書き込み速度と読み取り速度の保証はありません。

2つのプロセス(またはそのことについてはスレッド)のアクション間にはまったく関係がないと常に想定する必要があります。

ハードウェアレベルのバスロックは、正しく行わない限り発生しません。コンパイラ/ライブラリ/os/cpuが正しく動作するようにするのは難しいかもしれません。同期プリミティブは、それが正しく行われるように作成されています。

ロックすると安全になりますが、それほど難しくはありません。だからそれをするだけです。


@unknown-私の回答が投稿されてから、質問が多少変更されました。ただし、説明する動作は、プラットフォーム(ハードウェア、OS、ライブラリ、およびコンパイラ)に大きく依存します。

コンパイラに固有の命令を与えないと、実際には32ビットが一度に書き出される保証はありません。32ビットワードがワード境界に整列されていない状況を想像してみてください。この非整列アクセスはx86で受け入れられ、x68の場合、アクセスはCPUによって一連の整列アクセスに変換されます。

これらの操作の間に割り込みが発生する可能性があります。コンテキストスイッチが途中で発生した場合、一部のビットは書き込まれ、一部は書き込まれません。バン、あなたは死んでいる。

また、16ビットCPUまたは64ビットCPUについて考えてみましょう。どちらも今でも人気があり、必ずしも思ったとおりに機能するとは限りません。

したがって、実際には、「他のCPUコアが書き込まれた1/2のワードサイズの値を取得する」という状況が発生する可能性があります。同期を使用していない場合にこの種のことが起こると予想されるかのようにコードを記述します。

さて、あなたが完全な単語が書き出されることを確実にするためにあなたの書き込みを実行する方法があります。これらのメソッドは同期のカテゴリに分類され、同期プリミティブの作成は、ライブラリ、コンパイラ、OS、およびハードウェアの設計者に任せるのが最善のタイプです。特に移植性に関心がある場合(コードを移植したことがない場合でも、そうあるべきです)

于 2009-04-17T21:46:56.060 に答える
10

この問題は実際には一部の人々が議論したよりも深刻です。現在の x86 CPU ではメモリ書き込みがアトミックであるという Zifre の意見は正しいですが、それは急速になくなりつつあります。メモリ書き込みは 1 つのコアに対してのみアトミックです。他のコアでは書き込みが同じ順序で行われない可能性があります。

言い換えれば、あなたがそうするなら

a = 1;
b = 2;

CPU 2bでは、場所「a」の前に変更された場所が表示される場合があります。また、ネイティブ ワード サイズ (x32 プロセッサでは 32 ビット) より大きい値を書き込んでいる場合、書き込みはアトミックではありません。したがって、64 ビット書き込みの上位 32 ビットは、下位とは異なる時間にバスにヒットします。 32 ビットの書き込み。これにより、事態が非常に複雑になる可能性があります。

メモリバリアを使用すれば大丈夫です。

于 2009-04-22T04:34:55.873 に答える
7

どこかに施錠する必要があります。コード レベルでない場合は、ハードウェア メモリ キャッシュとバスで。

おそらく、PentiumPro 以降の Intel CPU でも問題ありません。私が今読んだところによると、Intel は後の CPU でマシンコードの LOCK プレフィックスを基本的に無視するようにしました。代わりに、キャッシュ コヒーレンシ プロトコルは、データがすべての CPU 間で一貫していることを確認します。そのため、コードがキャッシュ ラインの境界を越えないデータを書き込む場合、それは機能します。キャッシュラインをまたぐメモリ書き込みの順序は保証されていないため、複数ワードの書き込みは危険です。

x86 または x86_64 以外のものを使用している場合は問題ありません。Intel 以外の多くの CPU (およびおそらく Intel Itanium) は、明示的なキャッシュ コヒーレンシ マシン コマンドを使用してパフォーマンスを向上させます。(カスタム ASM コード、コンパイラ組み込み関数、またはライブラリを介して) それらを使用しない場合、キャッシュを介したメモリへの書き込みは保証されません。別の CPU から見えるようになったり、特定の順序で発生したりすることはありません

したがって、Core2 システムで何かが機能するからといって、コードが正しいとは限りません。移植性を確認したい場合は、PPC (古い MacPro または Cell ブレード)、Itanium、IBM Power、ARM などの他の SMP アーキテクチャーでもコードを試してください。Alpha は、悪い SMP コードを明らかにするための優れた CPU でしたが、見つけられるとは思えません。

于 2009-04-17T22:24:17.750 に答える
3

メモリを介してデータを共有する場合、2 つのプロセス、2 つのスレッド、2 つの CPU、2 つのコアのすべてに特別な注意が必要です。

この IBM の記事では、オプションの優れた概要を説明しています。

Linux 同期方法の分析 カーネル アトミック、スピンロック、およびミューテックス 著 M. Tim Jones (mtj@mtjones.com)、コンサルタント エンジニア、Emulex

http://www.ibm.com/developerworks/linux/library/l-linux-synchronization.html

于 2009-04-25T13:25:55.653 に答える
2

最新のマイクロプロセッサにおける読み取りメモリの順序付け、パート Iおよびパート II

これが理論的に安全ではない理由の背景を説明します。

潜在的なレースは次のとおりです。

  • プロセス A (CPU コア A 上) が新しい共有メモリ領域に書き込みます
  • プロセス A は、その共有メモリ ID を共有 32 ビット変数に入れます (つまり、32 ビットでアラインされます。許可すると、コンパイラはこのようにアラインしようとします)。
  • プロセス B (CPU コア B 上) が変数を読み取ります。32 ビットのサイズと 32 ビットのアラインメントを想定すると、実際にはガベージが発生することはありません。
  • プロセス B は、共有メモリ領域からの読み取りを試みます。ここで、メモリバリアを逃したため、A が書き込んだデータが表示されるという保証はありません。(実際には、共有メモリ セグメントをマップするライブラリ コードで CPU B にメモリ バリアが存在する可能性があります。問題は、プロセス A がメモリ バリアを使用していないことです)。

また、この設計で共有メモリ領域を安全に解放する方法も明確ではありません。

最新のカーネルと libc を使用すると、pthreads ミューテックスを共有メモリ領域に配置できます。(これには NPTL の最新バージョンが必要です - 私は Debian 5.0 "lenny" を使用していますが、問題なく動作します)。共有変数を単純にロックするだけで、難解なメモリ バリアの問題を心配する必要がなくなります。

于 2009-04-27T18:19:59.567 に答える
2

私は実際、これは完全に安全であると信じています(ただし、正確な実装に依存します)。「マスター」セグメントが基本的に配列であると仮定すると、shmid がアトミックに書き込むことができ (32 ビットの場合はおそらく問題ありません)、2 番目のプロセスが読み取りだけであれば問題ありません。ロックは、両方のプロセスが書き込みを行っている場合、または書き込まれる値をアトミックに書き込むことができない場合にのみ必要です。破損した (半分書き込まれた値) ことはありません。もちろん、これを処理できない奇妙なアーキテクチャがいくつかあるかもしれませんが、x86/x64 では問題ないはずです (おそらく ARM、PowerPC、およびその他の一般的なアーキテクチャも同様です)。

于 2009-04-17T22:33:03.017 に答える
1

あなたがこれを求めているなんて信じられません。いいえ、必ずしも安全ではありません。少なくとも、これは、shmidを設定するときに共有メモリの場所をアトミックに設定するコードをコンパイラが生成するかどうかによって異なります。

今、私はLinuxを知りませんが、shmidは16から64ビットだと思います。つまり、少なくともすべてのプラットフォームに、この値をアトミックに書き込むことができる命令がある可能性があります。しかし、どういうわけか尋ねられずにこれを行うコンパイラに依存することはできません。

メモリ実装の詳細は、プラットフォーム固有のものの1つです。

ところで、あなたの場合は問題ではないかもしれませんが、一般的に、単一のCPUシステムであっても、ロックについて心配する必要があります。一般に、一部のデバイスは共有メモリに書き込むことができます。

于 2009-04-17T21:40:08.230 に答える
1

法的?私は考えます。あなたの「管轄区域」に依存します。安全で正気ですか?ほぼ間違いなくそうではありません。

編集:これをより多くの情報で更新します。

このウィキペディアのページをご覧になることをお勧めします。特に「リソースへのアクセスの調整」のセクション。特に、ウィキペディアの議論は本質的に自信の失敗を説明しています。共有リソースへのロックされていないアクセスは、アトミックリソースの場合でも、アクションが実行されたという確信の誤った報告/不実表示を引き起こす可能性があります。基本的に、リソースを変更できるかどうかを確認するまでの間に、リソースは外部から変更されるため、条件付きチェックに固有の信頼性が失われます。

于 2009-04-17T21:46:34.500 に答える
1

私はそれがうまくいくかもしれないことに同意します-それで安全かもしれませんが、正気ではありません. 主な質問は、この低レベルの共有が本当に必要かどうかです。私は Linux の専門家ではありませんが、OS がロック作業を行うように、たとえばマスター共有メモリ セグメントに FIFO キューを使用することを検討します。 . いずれにせよ、コンシューマー/プロデューサーは通常、同期のためにキューを必要とします。

于 2009-04-22T04:42:56.853 に答える
1

特にバス帯域幅に制約のあるシステムで、ロックの競合がバスに与える影響について、ここで誰も議論していないと思います。

これは、この問題に関する記事で、バスを介した排他的アクセスに対する全体的な要求を軽減する代替スケジューリング アルゴリズムについて説明しています。これにより、場合によっては単純なスケジューラよりも合計スループットが 60% 以上向上します (明示的なロック プレフィックス命令または暗黙的な xchg cmpx のコストを考慮すると..)。この論文は最新の作業ではなく、実際のコード (アカデミックなもの) の邪魔にはなりませんが、この問題を読んで検討する価値があります。

最近の CPU ABI は、単純な lock anything 以外の代替操作を提供ます

FreeBSD (多くの内部カーネル コンポーネントの作成者) のJeffrが、SSE3 に追加された 2 つの命令である monitor と mwait について説明します。単純なテスト ケースで 20% の改善が確認されました。彼は後に仮定します。

これが適応アルゴリズムの最初の段階です。しばらくスピンしてから、高電力状態でスリープし、負荷に応じて低電力状態でスリープします。

...

ほとんどの場合、hlt でもまだアイドリングしているため、電力に悪影響はないはずです。実際、アイドル状態に出入りするために多くの時間とエネルギーを浪費するため、必要な合計 CPU 時間を削減することで、負荷時の電力を改善できる可能性があります。

hlt の代わりに一時停止を使用すると、どのような効果があるのだろうか。

Intel の TBBから。

        ALIGN 8
        PUBLIC __TBB_machine_pause
__TBB_machine_pause:
L1:
        dw 090f3H; pause
        add ecx,-1
        jne L1
        ret
end

Art of Assemblyは、ロック プレフィックスまたは xchg を使用しない同期化も使用します。私はしばらくその本を読んでおらず、ユーザーランドの保護モード SMP コンテキストでの適用可能性について直接話すつもりはありませんが、一見の価値があります。

幸運を!

于 2009-05-29T10:24:27.503 に答える
0

Reader-Writer Lock が必要なようですね: http://en.wikipedia.org/wiki/Readers-writer_lock

于 2009-04-22T13:41:57.633 に答える
-1

答えは、読み取りと書き込みを同時に行うことは絶対に安全です。

shm メカニズムが必要最小限のツールをユーザーに提供することは明らかです。すべてのアクセス制御は、プログラマーが処理する必要があります。ロックと同期はカーネルによって親切に提供されています。これは、ユーザーが競合状態についてあまり心配しないことを意味します。このモデルは、プロセス間でデータを共有する対称的な方法のみを提供することに注意してください。プロセスが別のプロセスに新しいデータが共有メモリに挿入されたことを通知したい場合、シグナル、メッセージ キュー、パイプ、ソケット、またはその他のタイプの IPC を使用する必要があります。

Linux記事の共有メモリから。

最新の Linux shm 実装では、メモリ バスと内部的に同期されている呼び出しとcopy_to_user使用のみが行われます。copy_from_user

于 2009-04-27T17:47:58.770 に答える