14

mallocとfreeフックを使用して、アプリケーションでのmallocとfreeの使用を監視したいと思います。

これがドキュメントですhttp://www.gnu.org/s/libc/manual/html_node/Hooks-for-Malloc.html

my_malloc_hookサンプルページから、mallocを再度呼び出す前に、mallocフックを一時的にオフ(またはチェーン内の前のフック)に切り替えることがわかります。

これは、マルチスレッドアプリケーションを監視する場合の問題です(説明については、質問の最後を参照してください)。

私がインターネットで見つけたmallocフックの使用の他の例にも同じ問題があります。

マルチスレッドアプリケーションで正しく機能するようにこの関数を書き直す方法はありますか?

たとえば、mallocフックが呼び出すことができる内部libc関数があり、フックを非アクティブ化する必要なしに、割り当てを完了します。

会社法上の理由でlibcのソースコードを見ることができないので、答えは明らかかもしれません。

私の設計仕様では、mallocを別のmalloc設計に置き換えることはできません。

他のフックは機能していないと推測できます。


アップデート

mallocのサービス中にmallocフックが一時的に削除されるため、別のスレッドがmallocを呼び出し、フックを取得できない場合があります。

これが起こらないようにmallocの周りに大きなロックがあることが示唆されていますが、それは文書化されていません。また、mallocを効果的に再帰的に呼び出すという事実は、フックの後にロックが存在するか、または愉快に賢くなければならないことを示唆しています。

caller -> 
  malloc -> 
    malloc-hook (disables hook) -> 
      malloc -> # possible hazard starts here
        malloc_internals
      malloc <-
    malloc-hook (enables hook) <-
  malloc
caller
4

4 に答える 4

13

更新しました

__malloc_hooksを信頼しないのは正しいことです。私はコードをちらっと見ました、そしてそれらは-驚くほど狂ったように-スレッドセーフではありません。

継承されたフックを直接呼び出すことは、mallocを復元して再入力するのではなく、引用するドキュメントから少し逸脱しているように思われます。

http://manpages.sgvulcan.com/malloc_hook.3.phpから:

フック変数はスレッドセーフではないため、現在は非推奨になっています。代わりに、プログラマーは、「malloc」や「free」などの関数を定義してエクスポートすることにより、関連する関数の呼び出しをプリエンプトする必要があります。

デバッグmalloc/realloc / free関数を挿入する適切な方法は、これらの関数の「デバッグ」バージョンをエクスポートする独自のライブラリを提供し、それ自体を実際の関数に延期することです。Cリンクは明示的な順序で行われるため、2つのライブラリが同じ機能を提供する場合は、最初に指定されたものが使用されます。LD_PRELOADメカニズムを使用して、UNIXのロード時にmallocを注入することもできます。

http://linux.die.net/man/3/efenceは、電気柵について説明しており、これらの両方のアプローチについて詳しく説明しています。

これらのデバッグ機能で必要な場合は、独自のロックを使用できます。

于 2010-01-07T14:54:08.010 に答える
3

私も同じ問題を抱えてる。私はその例でそれを解決しました。THREAD_SAFEを定義しない場合、manによって与えられた例があり、セグメンテーションエラーが発生します。THREAD_SAFEを定義すると、セグメンテーションエラーは発生しません。

#include <malloc.h>
#include <pthread.h>

#define THREAD_SAFE
#undef  THREAD_SAFE

/** rqmalloc_hook_  */

static void* (*malloc_call)(size_t,const void*);

static void* rqmalloc_hook_(size_t taille,const void* appel)
{
void* memoire;

__malloc_hook=malloc_call; 
memoire=malloc(taille);    
#ifndef THREAD_SAFE
malloc_call=__malloc_hook;   
#endif
__malloc_hook=rqmalloc_hook_; 
return memoire;
}

/** rqfree_hook_ */   

static void  (*free_call)(void*,const void*);

static void rqfree_hook_(void* memoire,const void* appel)
{
__free_hook=free_call;   
free(memoire);            
#ifndef THREAD_SAFE
free_call=__free_hook;    
#endif
__free_hook=rqfree_hook_; 
}

/** rqrealloc_hook_ */

static void* (*realloc_call)(void*,size_t,const void*);

static void* rqrealloc_hook_(void* memoire,size_t taille,const void* appel)
{
__realloc_hook=realloc_call;     
memoire=realloc(memoire,taille); 
#ifndef THREAD_SAFE
realloc_call=__realloc_hook;    
#endif
__realloc_hook=rqrealloc_hook_; 
return memoire;
}

/** memory_init */

void memory_init(void)
{
  malloc_call  = __malloc_hook;
  __malloc_hook  = rqmalloc_hook_;

  free_call    = __free_hook;
  __free_hook    = rqfree_hook_;

  realloc_call = __realloc_hook;
  __realloc_hook = rqrealloc_hook_;
 }

 /** f1/f2 */

 void* f1(void* param)
 {
 void* m;
 while (1) {m=malloc(100); free(m);}
 }

 void* f2(void* param)
 {
 void* m;
 while (1) {m=malloc(100); free(m);}
 }

 /** main */
 int main(int argc, char *argv[])
 {
 memory_init();
 pthread_t t1,t2;

 pthread_create(&t1,NULL,f1,NULL);
 pthread_create(&t1,NULL,f2,NULL);
 sleep(60);
 return(0);
 }
于 2010-01-20T07:51:29.567 に答える
2

malloc()へのすべての呼び出しはフックを経由するため、セマフォで同期できます(解放されるまで待機し、ロックし、フックを調整して、セマフォを解放します)。

[編集]IANALしかし...コードでglibcを使用できる場合は、コードを見ることができます(LGPLであるため、それを使用する人は誰でもソースのコピーを持つことが許可されている必要があります)。したがって、法的な状況を正しく理解しているかどうか、または会社からglibcの使用が法的に許可されていないかどうかはわかりません。

[EDIT2]少し考えてみると、呼び出しパスのこの部分は、glibcが作成するある種のロックによって保護されている必要があると思います。そうしないと、マルチスレッドコードでフックを使用しても確実に機能しなくなり、ドキュメントでこれについて言及されると確信しています。malloc()スレッドセーフである必要があるため、フックも同様である必要があります。

それでも心配な場合は、ループ内でメモリを割り当てて解放する2つのスレッドを備えた小さなテストプログラムを作成することをお勧めします。フックのカウンターをインクリメントします。百万ラウンド後、カウンターは正確に200万になるはずです。これが当てはまる場合、フックもmalloc()ロックによって保護されます。

[EDIT3]テストが失敗した場合、法的な事情により、モニターを実装することはできません。上司に伝えて、上司に決定を任せましょう。

[EDIT4]グーグルはバグレポートからこのコメントを見つけました:

フックはスレッドセーフではありません。限目。何を修正しようとしていますか?

これは、修正が含まれているバグに関する2009年3月からの議論の一部ですlibc/malloc/malloc.cしたがって、この日付以降のバージョンのglibcは機能する可能性がありますが、保証はないようです。また、GCCのバージョンによっても異なるようです。

于 2010-01-07T14:51:20.213 に答える
2

mallocに再帰している間、スレッドセーフな方法でmallocフックを使用する方法はありません。インターフェイスの設計が不適切で、おそらく修復できない可能性があります。

フックコードにミューテックスを入れても、問題は、への呼び出しがmallocフックメカニズムを通過するまでそれらのロックを認識せず、フックメカニズムを通過するために、グローバル変数(フックポインター)を参照することです。ミューテックスを取得せずに。あるスレッドでこれらのポインターを保存、変更、および復元しているときに、別のスレッドでのアロケーター呼び出しはそれらの影響を受けます。

主な設計上の問題は、フックがデフォルトでnullポインタであるということです。インターフェイスが、適切なアロケータであるnull以外のデフォルトフック(これ以上フックを呼び出さない最下位のアロケータ)を提供するだけの場合、フックを追加するのは簡単で安全です。前のフックを保存するだけで済みます。新しいフックでは、グローバルポインターをいじることなく、holdフックを呼び出してmallocに戻ります(フックのインストール時を除き、スレッドが起動する前に実行できます)。

あるいは、glibcは、フックを呼び出さない内部mallocインターフェースを提供することもできます。

もう1つの適切な設計は、フックにスレッドローカルストレージを使用することです。フックのオーバーライドと復元は、別のスレッドから見たフックを乱すことなく、1つのスレッドで実行されます。

現状では、glibcのmallocフックを安全に使用するためにできることは、mallocへの再発を回避することです。フックコールバック内のフックポインタを変更せず、単に独自のアロケータを呼び出します。

于 2014-03-06T01:51:54.910 に答える