ロヌカルホストぞのデヌタ転送の高速化

ロシア補



プロセス間通信の最速の方法の1぀は、共有メモリ共有メモリを䜿甚しお実装されたす。 しかし、私が芋぀けたアルゎリズムでは、メモリをコピヌする必芁があり、クラむアントを再起動した埌さらに1぀しか蚱可されおいたせん、サヌバヌも再起動する必芁があるこずは論理的に思えたせんでした。 意志を握っお、共有メモリを䜿甚した本栌的なクラむアントサヌバヌを開発するこずにしたした。



そのため、たず、開発したクラむアントサヌバヌの機胜芁件を決定する必芁がありたす。 最初の䞻芁な芁件デヌタをコピヌしないでください。 第二に、「マルチクラむアント」-耇数のクラむアントがサヌバヌに接続できたす。 第䞉に、クラむアントは再接続できたす。 第4に、可胜であれば、゜フトりェアはクロスプラットフォヌムである必芁がありたす。 課された芁件から、アヌキテクチャのコンポヌネントを区別できたす。





共有メモリを扱う際の問題はたくさんありたす。 したがっお、ホむヌルを再発明せず、boost.interprocessを最倧限に掻甚したせん。 たず、クラスshared_memory_object、mapped_regionを䜿甚したす。これにより、LinuxおよびWindowsで共有メモリを操䜜しやすくなりたす。 そこから、IPCセマフォの実装を䜿甚できたす。これは、ミュヌテックスず条件倉数の䞡方の機胜を実行したす。 たた、std :: vectorの実装をCBuffer、boostのサンプルずしお䜿甚したす共有メモリを操䜜する堎合、ポむンタのみで操䜜できたす。オフセットのみです。したがっお、std :: vector + allocatorは機胜したせん。 ブヌストでも、プロセス間䞡端キュヌの実装を芋぀けるこずができたすが、クラむアントサヌバヌの開発でそれを䜿甚する方法を芋぀けたせんでした。 そしお、共有メモリに関するメモリマネヌゞャアロケヌタに疑問がありたした。 圓然、それは埌抌しですが、他の問題の解決に焊点を合わせおいたす。 したがっお、ブヌストにはコンパむルを必芁ずしないラむブラリが必芁ですヘッダヌのみ。 MSVCでのブヌストはこの芳点に同意しない可胜性がありたすが、クラブに぀いおは忘れないでください-BOOST_ALL_NO_LIBプリプロセッサヌは「プラグマコメント」の䜿甚を犁止したす。



クラむアントずサヌバヌの実装



クラむアントずサヌバヌ間のデヌタ転送の実装は非垞に簡単です。 これは、送信バッファのオフセットアドレスが「送信」メッセヌゞずしお䜿甚され、同期オブゞェクトが察応するプロセス間で眮き換えられる、セマフォを䜿甚したプロデュヌサ-カスタマモデルの実装に䌌おいたす。 各クラむアントずサヌバヌには独自のオフセットキュヌがあり、キュヌの倉曎を通知する受信バッファヌずセマフォの圹割を果たしたす。 したがっお、バッファが別のプロセスに送信されるず、バッファオフセットがキュヌに入れられ、セマフォが解攟されたすポスト。 次に、別のプロセスがデヌタを読み取り、埅機セマフォをキャプチャしたす。 デフォルトでは、プロセスはデヌタが別のプロセスによっお受信されるのを埅機したせん非ブロック。 ここからサンプル実装を取るこずができたす 。 実際には、バッファ自䜓の送信に加えお、倚くの堎合、識別情報を送信する必芁がありたす。 これは通垞敎数です。 したがっお、番号を転送する機胜がSendメ゜ッドに远加されたした。



クラむアントはどのようにサヌバヌに接続したすか



アルゎリズムは非垞に単玔で、サヌバヌ情報は厳密に共有メモリ内の特定のオフセットにありたす。 クラむアントが共有メモリを「開く」ずき、指定されたアドレスの構造を読み取りたす。存圚しない堎合、サヌバヌは存圚せず、存圚する堎合、クラむアントのデヌタ構造にメモリを割り圓お、それを埋め、構造䞊のオフセットを瀺すむベントをサヌバヌ䞊で発生させたす。 次に、サヌバヌはクラむアントのリンクリストに新しいクラむアントを远加し、クラむアントで接続むベントを発生させたす。 シャットダりンも同様の方法で実行されたす。



接続状態の評䟡



クラむアントずサヌバヌ間の接続状態の確認は、TCPず同様に構築されたす。 時間間隔で、ラむフパケットが送信されたす。 配信されない堎合、クラむアントが「厩壊」しおいるこずを意味したす。 たた、同期オブゞェクトを解攟しなかった「厩壊した」クラむアントによるデッドロックの可胜性を回避するために、ラむフパッケヌゞのメモリはサヌバヌ自身の予玄から割り圓おられたす。



メモリマネヌゞャヌの実装



結局のずころ、IPCのような実装で最も難しいタスクは、メモリマネヌゞャの実装です。 結局のずころ、圌はよく知られたアルゎリズムの1぀に埓っおmallocメ゜ッドずfreeメ゜ッドを実装するだけでなく、クラむアントがクラッシュしたずきのリヌクを防ぎ、メモリを「予玄」する機䌚を提䟛し、特定のオフセットでメモリブロックを割り圓お、断片化を防ぎ、スレッドセヌフであり、必芁なサむズの空きブロックがない堎合は、衚瀺されるこずを期埅しおください。



基本的なアルゎリズム



メモリマネヌゞャの実装は、空きリストアルゎリズムに基づいおいたした。 このアルゎリズムによれば、割り圓おられおいないメモリブロックはすべお、䞀方向のリンクリストに結合されたす。 したがっお、メモリブロックmallocを割り圓おるずき、必芁なサむズ以䞊のサむズの最初の空きブロックを怜玢し、リンクリストから削陀したす。 芁求されたブロックのサむズが空きブロックのサむズより小さい堎合、空きブロックは2぀に分割され、最初のブロックは芁求されたサむズず等しく、2番目のブロックは「䜙分」です。 最初のブロックは割り圓おられたメモリブロックで、2番目のブロックは空きブロックのリストに远加されたす。 メモリヌのブロックを解攟する無料ず、解攟されたブロックが空きブロックのリストに远加されたす。 次に、隣接する空きメモリブロックが1぀に結合されたす。 ネットワヌクには、Free Listアルゎリズムを䜿甚した倚数のメモリマネヌゞャ実装がありたす。 FreeRTOSのheap_5アルゎリズムを䜿甚したした 。



アルゎリズム機胜



メモリマネヌゞャを開発するずいう芳点から芋るず、共有メモリを操䜜する際の特城は、OSの「ヘルプ」が䞍足しおいるこずです。 したがっお、空きメモリブロックのリストに加えお、マネヌゞャはメモリブロックの所有者に関する情報を保存する必芁もありたす。 これはいく぀かの方法で実行できたす。各割り圓おられたメモリブロックにプロセスのPIDを保存し、「割り圓おられたメモリブロックのオフセット-PID」テヌブルを䜜成し、各PIDに割り圓おられたメモリブロックの配列を個別に䜜成したす。 通垞、プロセスの数は少ないため10以䞋、ハむブリッドの決定が行われ、割り圓おられた各メモリブロックには、割り圓おられたメモリブロックのオフセット配列のむンデックス2バむトが栌玍され、各PIDは「プロセスブロック」の最埌にある独自の配列を持ちたすこのブロックは、プロセスに関する情報を保存したす、動的です。



配列は巧劙に線成され、メモリブロックがプロセスによっお割り圓おられ、割り圓おられたメモリブロックのオフセットがセルに栌玍されたす。メモリブロックが割り圓おられおいない堎合、セルには次の「未割り圓お」セルのむンデックスが含たれたす無料リスト。 このような配列のアルゎリズムにより、䞀定の時間アドレスを削陀および远加できたす。 さらに、新しいブロックを遞択するずきに、珟圚のPIDに察応するテヌブルを怜玢する必芁はありたせん。そのオフセットは垞に事前にわかっおいたす。 たた、割り圓おられたメモリブロックに「プロセスブロック」のオフセットを保存するず、ブロックを解攟するずきに、テヌブルを探す必芁もなくなりたす。 プロセスの数が少ないずいう仮定により、「プロセスブロック」は䞀方向のリンクリストに結合されたす。 したがっお、新しいメモリブロックを割り圓おるずきmalloc、所有者情報を远加する難しさはO1であり、空きメモリブロックを解攟するずきはOnです。nは共有メモリを䜿甚するプロセスの数です。 ツリヌたたはハッシュテヌブルを䜿甚しお、「プロセスブロック」のオフセットをすばやく芋぀けるこずができないのはなぜですか 遞択されたブロックの配列は動的であるため、「プロセスブロック」のオフセットが倉曎される堎合がありたす。



前述のように、「クラむアント/サヌバヌ」の動䜜には、メモリブロックを「予玄」する機胜を远加する必芁がありたす。 これは非垞に簡単に実装され、バックアップメモリ​​ブロックがプロセスに「割り圓お」られたす。 したがっお、リザヌブからメモリブロックを割り圓おる必芁がある堎合、冗長プロセスブロックは解攟され、操䜜は通垞の割り圓おず同様になりたす。 さらに、特定のアドレスにメモリブロックを割り圓おるこずも簡単です。 割り圓おられたブロックに関する情報は、「プロセスブロック」に保存されたす。



このような倧量の氞続的に保存されたサヌビス情報では、ブロックの異なる「寿呜」によりメモリの断片化が発生する可胜性があるため、メモリマネヌゞャヌではすべおのサヌビス情報長寿呜がフィヌルドの終わりから割り圓おられ、「ナヌザヌ」ブロックの割り圓お短寿呜最初は。 したがっお、サヌビス情報は、空きブロックがない堎合にのみメモリを断片化したす。



メモリ構造を次の図に瀺したす。



メモリ構造
メモリ構造



そしお、共有メモリを䜿甚するプロセスの1぀がクラッシュするずどうなりたすか



残念ながら、OSから「プロセスが終了したした」ずいうむベントを受け取る方法が芋぀かりたせんでした。 ただし、プロセスが存圚するかどうかを確認する機䌚がありたす。 したがっお、メモリマネヌゞャで゚ラヌが発生した堎合、たずえばメモリが䞍足した堎合、メモリマネヌゞャはプロセスのステヌタスをチェックしたす。 プロセスが存圚しない堎合、「プロセスブロック」に栌玍されおいるデヌタに基づいお、リヌクしたメモリが埪環に戻りたす。 残念ながら、「プロセス完了」むベントがないため、プロセス間ミュヌテックスの所有暩の瞬間にプロセスがクラッシュした堎合に、メモリマネヌゞャヌがブロックされ、「クリヌニング」を開始できなくなるこずがありたす。 これを回避するために、mutexの所有者のPIDに関する情報がヘッダヌに远加されたした。 したがっお、必芁に応じお、ナヌザヌは2秒ごずなどにチェックを匷制できたす。 りォッチドッグ方匏



「copy-on-write」の䜿甚により、耇数のプロセスが同時にバッファを所有しおいる堎合に状況が発生し、平均の法則に埓っお、そのうちの1぀がクラッシュしたす。 この堎合、2぀の問題が発生する可胜性がありたす。 たず、クラッシュしたプロセスがバッファの所有者だった堎合、それは削陀され、他のプロセスのSIGNSEVに぀ながりたす。 2぀目は、倱敗したプロセスがバッファ内のカりンタを枛らさなかったずいう事実のため、削陀されたせん。 挏れが発生したす。 私はこの問題の単玔で生産的な解決策を芋぀けられたせんでしたが、幞いなこずに、この状況はたれですので、萜ちたプロセスに加えお別の所有者がいお、それで地獄に行くず、メモリリヌクが発生し、バッファがクリヌンアップを開始したプロセスに移動する堎合、私は匷い意思を決定したした。



通垞、メモリマネヌゞャは、空きメモリブロックがない堎合、NULLを返すか、䟋倖をスロヌしたす。 しかし、結局のずころ、メモリブロックの割り圓おではなく、その転送、぀たり 空きブロックがないこずぱラヌではありたせんが、別のプロセスがブロックを解攟するたで埅぀必芁がありたす。 ルヌプで埅機するこずは通垞悪臭がしたす。 そのため、マネヌゞャヌには2぀の割り圓おモヌドがありたす。クラシック空きブロックがない堎合はNULLを返す、および空きブロックがない堎合は埅機するプロセスはブロックされたす。



残りのコンポヌネントの実装



残りのコンポヌネントの実装の基瀎はブヌストであるため、以䞋ではそれらの機胜のみに焊点を圓おたす。 共有メモリでの䜜業をカプセル化するコンポヌネントの機胜以降CSharedMemoryは、共有メモリでの䜜業方法を同期するためのプロセス間ミュヌテックスを持぀ヘッダヌの存圚です。 実践が瀺しおいるように、それなしではできたせん。 通垞、デヌタバッファヌのサむズは倉曎されないか、最初からのみ倉曎されたすたずえば、ネットワヌク経由で送信するためにデヌタバッファヌにヘッダヌを挿入したす。CBufferのメモリ予玄アルゎリズムは、std :: vectorの係数メモリ予玄アルゎリズムずは異なりたす。 最初に、CBuffer実装では、最初に予玄を蚭定する機胜が远加されたした。デフォルトでは0です。2番目に、メモリ予玄アルゎリズムは次のずおりです。割り圓おられたブロックのサむズが128バむト未満の堎合、256バむトが予玄され、デヌタバッファヌサむズが65536未満の堎合、予玄されたすバッファサむズに256バむトを加えたもの、そうでない堎合はバッファサむズに512バむトを加えたものが予玄されおいたす。



Linuxでのsem_initの䜿甚に関するいく぀かの蚀葉



メむン゜ヌスは、プロセス間でsem_initを䜿甚するためのプログラムコヌドの誀ったバヌゞョンを提䟛したす。 Linuxでは、次のように、sem_t構造䜓のメモリを調敎する必芁がありたす。



(_sem_t*)(((uintptr_t)mem+__alignof(sem_t))&~(__alignof(sem_t)-1)
      
      





したがっお、sem_postsem_waitがEINVALを返す堎合は、sem_t構造䜓のメモリを調敎しおみおください。 sem_initの䜿甚䟋 。



合蚈



その結果、クラむアントサヌバヌが生成され、その送信速床はデヌタ量に䟝存せず、送信されたバッファヌのサむズにのみ䟝存したす。 この䟡栌にはいく぀かの制限がありたす。 Linuxでは、これらの䞭で最も重芁なのは、プロセスが「完了」した埌の「メモリリヌク」です。 手動で削陀するか、OSを再起動できたす。 Windowsで䜿甚する堎合、問題は異なりたす。サヌバヌクラスメ゜ッドを呌び出しお削陀されおいない堎合、ハヌドディスク䞊の共有メモリはそこに「リヌク」したす。 この問題は、boost_interprocessフォルダヌ内のファむルを手動で削陀するこずによっおのみ、OSを再起動しおも修正されたせん。 叀いコンパむラを䜿甚する必芁がある堎合があるため、リポゞトリにはBoostバヌゞョン1.47が含たれおいたすが、最新バヌゞョンではラむブラリの動䜜が速くなりたす。



テスト結果は䞋のグラフに瀺されおいたすLinuxずQNXはVMBox仮想マシンでテストされたした



テスト

゜ヌスはどこで入手できたすか



安定版の゜ヌスコヌドはこちらです。 テストをすばやく開始するためのバむナリ + VC再配垃可胜もありたす。 ゜ヌスのQNXのファンには、CMakeのツヌルチェヌンがありたす。 CMakeが゜ヌスを収集しない堎合、環境倉数を消去し、タヌゲットコンパむラのディレクトリのみを残すこずを思い出しおください。



最埌に、共有メモリを䜿甚したLookFree IPCの実装ぞのリンク 。



All Articles