4

Windows XP の 32 ビット アドレス空間内で実行されている、複雑でメモリを大量に消費するマルチ スレッド アプリケーションを考えてみましょう。

特定の操作では、一度にアクセスする必要があるバッファは 1 つだけである、固定サイズの n 個の大きなバッファが必要です。

アプリケーションは、1 つのバッファーのサイズのアドレス空間が早期に予約され、現在必要なバッファーを格納するために使用されるパターンを使用します。

これは次の順序に従います: (最初の実行) VirtualAlloc -> VirtualFree -> MapViewOfFileEx (バッファの変更) UnMapViewOfFile -> MapViewOfFileEx

ここでは、バッファーの場所へのポインターが VirtualAlloc への呼び出しによって提供され、その同じ場所が MapViewOfFileEx への呼び出しごとに使用されます。

問題は、ウィンドウが (私が知る限り) 異なるユーザー間でメモリ空間を渡すためのハンドシェイク タイプの操作を提供しないことです。

したがって、メモリがロックされず、別のスレッドがジャンプしてバッファ内で割り当てを実行できる小さな機会があります (上記の各シーケンスで ->)。

MapViewOfFileEx への次の呼び出しは壊れており、システムはアドレス空間にバッファ用に十分な大きさの空間があることを保証できなくなりました。

より小さなバッファーを使用するようにリファクタリングすると、スペースの再割り当てに失敗する率が明らかに低下します。

HeapLock の一部の使用はある程度成功していますが、これにはまだ問題があります。何かがまだアドレス空間内からメモリを盗むことができます。(GetProcessHeaps を呼び出してから、HeapLock を使用してすべてのヒープをロックしようとしました)

MapViewOfFileEx と互換性のあるアドレス空間の特定のブロックをロックする方法はありますか?

編集:最終的にこのコードは、私の制御外のアプリケーションによって呼び出されるライブラリに存在することを追加する必要があります

4

4 に答える 4

1

あなたはそれをブルートフォースすることができます。マッピングを実行していないプロセス内のすべてのスレッドを中断し、マップ解除/再マップして、中断されたスレッドの中断を解除します。それはエレガントではありませんが、必要な種類の相互排除を提供するために私が思いついた唯一の方法です。

于 2009-12-01T08:01:56.163 に答える
0

以前の投稿で示唆されているように、メモリ マッピングを変更している間、プロセス内のすべてのスレッドを中断できます。そのために SuspendThread()/ResumeThread() を使用できます。これには、コードが他のすべてのスレッドを認識し、それらのスレッド ハンドルを保持する必要があるという欠点があります。

別の方法は、Windows デバッグ APIを使用してすべてのスレッドを中断することです。プロセスにデバッガーがアタッチされている場合、プロセスに障害が発生するたびに、デバッガーが障害を処理してプロセスを再開するまで、Windows はプロセスのすべてのスレッドを中断します。

非常によく似ていますが、表現が異なるこの質問も参照してください: Windows でメモリ マッピングをアトミックに置き換える

于 2012-12-13T04:54:07.200 に答える
0

を介して独自のプライベートヒープを作成することを検討しましたHeapCreateか? ヒープを目的のバッファ サイズに設定できます。残っている唯一の問題はMapViewOfFile、デフォルト ヒープの代わりにプライベート ヒープを使用する方法です。

デフォルトのヒープを取得するためにMapViewOfFile内部的に呼び出し、次に連続したメモリ ブロックを要求すると仮定します。への呼び出しを回り道でGetProcessHeap囲むことができます。つまり、メモリ内のメソッドを上書きして呼び出しを再配線し、プライベート ヒープを返すことができる独自のコードへのジャンプを効果的に挿入します。MapViewOfFileGetProcessHeap

Microsoft はDetour Libraryを公開していますが、私はあまりよく知りません。意外と迂回が多いのは知っています。セキュリティ ソフトウェア、ウイルス スキャナなどはすべて、このようなフレームワークを使用しています。きれいではありませんが、うまくいくかもしれません:

HANDLE g_hndPrivateHeap;

HANDLE WINAPI GetProcessHeapImpl() {
    return g_hndPrivateHeap;
}    


struct SDetourGetProcessHeap { // object for exception safety 
   SDetourGetProcessHeap() {
       // put detour in place
   }

   ~SDetourGetProcessHeap() {
       // remove detour again
   }
};


void MapFile() {
    g_hndPrivateHeap = HeapCreate( ... );

    {
        SDetourGetProcessHeap d;
        MapViewOfFile(...);
    }
}

これらも役立つ場合があります。

MS VC++ プロジェクトの WinAPI 関数呼び出しを独自の実装 (名前とパラメーター セットは同じ) に置き換える方法は?

C/C++ で Windows 関数をフックするにはどうすればよいですか?

http://research.microsoft.com/pubs/68568/huntusenixnt99.pdf

于 2009-11-24T13:18:00.673 に答える
0

次のようなコードを持ってあなたのところに来たと想像してみてください:

void *foo;

foo = malloc(n);
if (foo)
   free(foo);
foo = malloc(n);

それから私はあなたのところに来て言った、助けて! fooは 2 回目の割り当てで同じアドレスを持っていません!

私はクレイジーですよね?

なぜこれがうまくいかないのかについて、あなたはすでに明確な知識を示しているように思えます。マッピング先として明示的なアドレスを取る API のドキュメントで、アドレスが単なる提案であり、保証できないことがわかるのには理由があります。これはmmap()POSIX でも同様です。

アドレスの変更が問題にならないようにプログラムを作成することをお勧めします。つまり、バッファー内の数量へのポインターをあまり多く格納しないでください。格納する場合は、再割り当て後にそれらを修正します。に渡すバッファを処理する方法と同様ですrealloc()

のドキュメントでさえ、これをMapViewOfFileEx()明示的に示唆しています:

現在安全な (オペレーティング システムで使用されていない) アドレスを指定することはできますが、そのアドレスが長期にわたって安全であるという保証はありません。したがって、オペレーティング システムにアドレスを選択させることをお勧めします。この場合、メモリ マップド ファイルにポインターを格納せず、ファイル マッピングのベースからのオフセットを格納して、マッピングを任意のアドレスで使用できるようにします。

コメントから更新

その場合、次のことができると思います。

  • 連続するブロックにマップされません。おそらく、チャンクでマップし、中間関数を記述して、どちらから読み取り/書き込みを行うかを決定できますか?

  • 64ビットに移植してみてください。

于 2009-11-24T17:58:59.560 に答える