2

ジェフリー・リッチターとクリストフ・ナサールの著書 『Windows via C-C ++』で以下のメモに出くわしました。

次のコードを調べます:V

OID EXEFunc() {
PVOID pv = DLLFunc();
// Access the storage pointed to by pv...
// Assumes that pv is in EXE's C/C++ run-time heap
free(pv);
}
PVOID DLLFunc() {
// Allocate block from DLL's C/C++ run-time heap
return(malloc(100));
}

それで、あなたはどう思いますか?上記のコードは正しく機能しますか?DLLの機能によって割り当てられたブロックは、EXEの機能によって解放されますか?答えは:多分。示されているコードは、十分な情報を提供していません。EXEとDLLの両方がDLLC/ C ++ランタイムライブラリにリンクしている場合、コードは正常に機能します。ただし、一方または両方のモジュールが静的C / C ++ランタイムライブラリにリンクしている場合、freeの呼び出しは失敗します。

モジュールを静的Cランタイムにリンクするときに、ここでfreeの呼び出しが失敗する理由を理解できません。

誰かが無料が失敗する理由を説明できますか?ここで同様の質問が見つかりました: Cランタイムの静的リンクと動的リンクでのメモリ割り当て

しかし、ここではMrPhilTxと同じ疑問があります。すべてのヒープが同じアドレス空間にあるのではないでしょうか。

ありがとう!

4

4 に答える 4

6

DLLとEXEの両方がCランタイムに静的にリンクされている場合、2つのランタイムは単にお互いを認識していません。したがって、EXEとDLLの両方が、ランタイムの独自のコピー、独自のヒープおよびヒープメタデータを取得します。どちらの側も他のメタデータについて知りません。また、メモリを解放するときにデータを更新する安全な方法はありません。メタデータが不安定になり、最終的には失敗します(運が良ければ、すぐに失敗します)。

これが意味するのは、プロセスに少なくとも2つのヒープがあり、各ヒープには独自のルールとメタデータがあるということです。DLLがメモリを割り当てる正確な方法をEXEが知る方法はないため、メモリを解放する方法はありません。

すべてが動的にリンクされているときにヒープの共有を回避できる理由については、簡単です。プロセスにはCランタイムDLLのコピーが1つしかないため、すべてのDLLがそれにリンクすると、すべてのDLLが同じコードを呼び出します。同じメタデータ。

于 2012-08-31T16:55:57.370 に答える
3

あるアロケータからメモリを割り当てて、別のアロケータで解放することはできません。異なるアロケータは異なる内部実装を使用し、メモリブロックを割り当てなかったアロケータにメモリブロックを与えた結果は予測できません。

したがって、コードの2つのセクションが同じアロケータを使用しているという事実を知らない限り、コードの一方のセクションにメモリを割り当て、もう一方のセクションでメモリを解放することはできません。通常の解決策は、同じユニットがメモリの割り当てと解放の両方を確実に行うことです。freeあなたの例では、DLLは、独自のアロケータに解放される独自の関数を呼び出す代わりに、メインコードが呼び出すことができる「解放」関数を提供できます。

したがって、代わりにこれを行います。

OID EXEFunc() {
    PVOID pv = DLLFunc();
    // Access the storage pointed to by pv...
    // Assumes that pv is in EXE's C/C++ run-time heap
    DLLFreeFunc(pv);
}

...

PVOID DLLFunc() {
    // Allocate block from DLL's C/C++ run-time heap
    return(malloc(100));
}

DLLFreeFunc(PVOID x) {
    free(x);
}
于 2012-08-31T12:51:18.773 に答える
1

Linuxでは、プログラムはbrkおよびsbrkシステムコールを使用して、カーネルに追加のデータページを要求します。sbrkは、プログラムで使用できるデータセグメントを指すアドレスを返します。

mallocとfreeは、brkとsbrkによって返されたデータセグメントをヒープに変換して使用します。ヒープは、現在のプロセスのスペースにある大きなメモリブロックであり、必要に応じて小さなメモリブロックを要求して返すことができます。mallocおよびfreeへの多くの呼び出しは、システム呼び出しを行わないことに注意することが重要です。

これで、mallocとfreeがヒープを利用したい場合、ヒープへのポインターを取得する必要があります。このポインタは、静的データと呼ばれる別のデータセグメントに格納され、アプリケーションのロード時に割り当てられます。異なるDLL(またはLinux上の共有ライブラリ)が互いに衝突しないようにするために、各DLLには独自の静的データセクションがあります。

ここで、dllと実行可能ファイルの両方が独自のライブラリに静的にリンクされていると仮定します。このような場合、dllと実行可能ファイルは異なるヒープへのポインタを持ち、そのようなイベントではdllと実行可能ファイルの両方が独自のメモリを解放する必要があります。

ただし、Linuxでは、dllと実行可能ファイルの両方がmallocにアクセスし、共通のDLL(Linuxではlibc.so)を介して解放されます。このような場合、dllと実行可能ファイルの両方がlibcのヒープに効果的にアクセスしているため、実行可能ファイルはdllによって割り当てられたメモリを安全に解放できます。

いずれにせよ、dllが独自の無料機能を提供することをお勧めします。これは、DLLFuncによって返されたポインタを解放する必要があることを他に文書化していない場合です。

これはWindowsにも当てはまると思います。

于 2012-08-31T17:23:28.723 に答える
0

mallocコードは、との実装に大きく依存しfreeます。良い実装には問題はなく、悪い実装は確かに失敗します。との実用的なDLL実装を作成する方が間違いなく簡単ですmallocfree、静的ライブラリで作成することは不可能ではありません。

GlobalAlloc簡単な例は、呼び出しをとに直接転送する静的ライブラリGlobalFreeです。

于 2012-08-31T12:52:02.443 に答える