ページ粒度よりも細かい特定のメモリ位置への書き込みが発生した場合、強制的にクラッシュさせることができますか?

StackOverflow https://stackoverflow.com/questions/2484959

質問

パフォーマンス上の理由から、共有メモリを使用するプログラムを作成しています (代替としてソケットとパイプが評価されていますが、それらは私のタスクには十分な速度ではありません。一般的に、コピーを含む IPC メソッドは遅すぎます)。共有メモリ領域に、固定サイズの多くの構造体を書き込んでいます。構造体を共有メモリに書き込むプログラムが 1 つあり、そこから読み取るクライアントが多数あります。ただし、クライアントが書き込む必要があるメンバーは各構造体に 1 つあります (参照カウント。参照カウントはアトミックに更新されます)。他のすべてのメンバーはクライアントに対して読み取り専用である必要があります。

クライアントはその 1 つのメンバーを変更する必要があるため、共有メモリ領域を読み取り専用としてマップすることはできません。ただし、他のメンバーをいじってはいけません。また、これらのプログラムは C++ で書かれているため、メモリ破損の可能性があります。理想的には、あるクライアントが別のクライアントをクラッシュさせることができるだけ困難である必要があります。私が心配しているのはバグのあるクライアントだけであり、悪意のあるクライアントについては心配していないため、不完全な解決策は許容されます。

クライアントが使用するヘッダー内のメンバーを const として宣言することでクライアントの上書きを阻止することはできますが、これではメモリ破損 (バッファ オーバーフロー、不正なキャストなど) による上書きは防止できません。挿入できます カナリア, しかし、そうすると常にチェックするコストを支払わなければなりません。

参照カウントのメンバーを直接保存する代わりに、構造体を読み取り専用のマップされたページに保持しながら、実際のデータへのポインターを別のマップされた書き込み専用のページに保存することができます。これは機能します。指定されたデータに書き込もうとすると、OS はアプリケーションを強制的にクラッシュさせますが、書き込みしようとすると間接ストレージが望ましくない場合があります。 ロックフリーアルゴリズム, 別のレベルの間接化に従う必要があると、何かをアトミックに実行できるかどうかが変わる可能性があるためです。

メモリの小さな領域を書き込むとアプリが爆発するようにマークする方法はありますか?一部のプラットフォームにはハードウェア ウォッチポイントがあり、インライン アセンブリを使用してそのうちの 1 つをアクティブにできるかもしれませんが、32 ビット x86 では一度に 4 つまでに制限され、制限されているため、それぞれが構造体の一部しかカバーできません。 4バイトまで。また、プログラムのデバッグも面倒になります ;)

編集:見つけました このかなり目を見張るような紙, ただし、残念ながら、ECC メモリと修正された Linux カーネルを使用する必要があります。

役に立ちましたか?

解決

OSレベルでそのように数ビットを読み取り専用にすることは不可能だと思います。

今私が思いついたのは、あなたが提案したように、参照カウントを別のページに置くことができるということです。構造体が共通のサイズで、すべてが連続したメモリ位置にある場合は、構造体内にポインタを持つのではなく、ポインタ演算を使用して構造体ポインタから参照カウントを見つけることができます。これは、ユースケースのポインタを用意するよりも優れている可能性があります。

long *refCountersBase;//The start address of the ref counters page
MyStruct *structsBase;//The start address of your structures page

//get address to reference counter
long *getRefCounter(MyStruct *myStruct )
{
    size_t n = myStruct - structsBase;
    long *ref = refCountersBase + n;
    return ref;
}

他のヒント

例外から回復する SIGSEGV のシグナル ハンドラーを追加する必要がありますが、これは特定のアドレスに対してのみです。出発点として考えられるのは、 http://www.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html およびお使いの OS に対応するドキュメントを参照してください。

編集: あなたが望むのは、書き込みを実行して書き込みアドレスが実際にOKである場合に戻り、例外を伝播したい場合は前の例外ハンドラー(例外ハンドラーをインストールするときに取得するポインター)を末尾呼び出しすることだと思います。ただし、私はこれらのことについては経験がありません。

ページ単位未満で読み取り専用を強制するという話は聞いたことがないので、各構造体を 2 ページに配置できない限り、その方向では運が悪いかもしれません。構造体ごとに 2 ページを用意できる場合は、一方のページに参​​照カウントを設定し、もう一方のページを読み取り専用にすることができます。

ヘッダーを使用するだけでなく、API を作成することもできます。クライアントに API の使用を強制すると、ほとんどの破損の問題が解決されます。

データを別のページではなく参照カウントとともに保持すると、データの局所性が向上し、キャッシュのパフォーマンスが向上します。

リーダーに問題があり、参照カウントを適切に更新できない可能性があることを考慮する必要があります。また、ライターがアップデートを完了できない可能性もあります。これらに対処するには、追加のチェックが必要です。このようなチェックを API と組み合わせることができます。ある種の整合性チェックによるパフォーマンスへの影響を測定するために実験してみる価値はあるかもしれません。adler32 のような単純なものであれば、チェックサムを保持するには十分な速度である可能性があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top