関数はmalloc()
再入可能ですか?
12 に答える
質問:「mallocは再入可能ですか?」
回答:いいえ、そうではありません。これが、ルーチンを再入可能にするものの1つの定義です。
mallocの一般的なバージョンでは、(シグナルハンドラーなどから)再入力することはできません。再入可能ルーチンはロックを使用しない場合があり、存在するほとんどすべてのmallocバージョンは、ロック(スレッドセーフにする)またはグローバル/静的変数(スレッドセーフで再入不可にする)を使用することに注意してください。
これまでのすべての回答は、「mallocはスレッドセーフですか?」と答えていますが、これはまったく別の質問です。その質問に対する答えは、ランタイムライブラリに依存し、場合によっては使用するコンパイラフラグに依存するということです。最新のUNIXでは、デフォルトでスレッドセーフなmallocを取得します。Windowsでは、、、またはフラグを使用して、スレッドセーフなランタイムライブラリを取得します。/MT
/MTd
/MD
/MDd
-pthreadを使用してコンパイルすると、mallocがスレッドセーフになることをどこかで読みました。ただし、mallocはANSI Cであり、スレッドはそうではないため、実装に依存していると確信しています。
gccについて話している場合:
コンパイルおよび-pthreadとのリンクおよびmalloc()は、x86およびAMD64でスレッドセーフになります。
別の意見、より洞察力
glibc-2.2 +の{malloc、calloc、realloc、free、posix_memalign}はスレッドセーフです
http://linux.derkeiler.com/Newsgroups/comp.os.linux.development.apps/2005-07/0323.html
これはかなり古い質問であり、現状に応じて新鮮さをもたらしたいと思います。
はい、現在malloc()
スレッドセーフです。
のGNUCライブラリリファレンスマニュアルからglibc-2.20 [released 2014-09-07]
:
void * malloc (size_t size)
暫定版:MT-Safe | ..。
... 1.2.2.1 POSIXの安全概念:
... MT-SafeまたはThread-Safe関数は、他のスレッドが存在する場合でも安全に呼び出すことができます。MTは、MT-Safeで、MultiThreadの略です。
MT-Safeであることは、関数がアトミックであることを意味するものでも、POSIXがユーザーに公開するメモリ同期メカニズムを使用するものでもありません。MT-Safe関数を順番に呼び出しても、MT-Safeの組み合わせが得られない可能性もあります。たとえば、スレッドが2つのMT-Safe関数を次々に呼び出すことは、他のスレッドでの同時呼び出しが破壊的な方法で干渉する可能性があるため、両方の関数の組み合わせのアトミック実行と同等の動作を保証しません。
ライブラリインターフェイス全体で関数をインライン化できるプログラム全体の最適化は、安全でない並べ替えを公開する可能性があるため、GNUCライブラリインターフェイス全体でインライン化を実行することはお勧めしません。文書化されたMT-Safetyステータスは、プログラム全体の最適化を保証するものではありません。ただし、ユーザーに表示されるヘッダーで定義された関数は、インライン化しても安全になるように設計されています。
これはglibcのmalloc.cからの抜粋です:
スレッドセーフ:NO_THREADSが定義されていない限り、スレッドセーフ
NO_THREADSがデフォルトで定義されていないと仮定すると、mallocは少なくともLinuxではスレッドセーフです。
はい、POSIX.1-2008では malloc
スレッドセーフです。
2.9.1スレッドセーフ
POSIX.1-2008のこのボリュームで定義されているすべての関数は、次の関数1がスレッドセーフである必要がないことを除いて、スレッドセーフである必要があります。
[含まない関数のリスト
malloc
]
GLIBCを使用している場合、答えは次のとおりです。はい、しかし。
具体的には、はい。ただし、mallocとfreeはスレッドセーフですが、デバッグ機能はそうではないことに注意してください。
特に、非常に便利なmtrace()、mcheck()、およびmprobe()関数はスレッドセーフではありません。GNUプロジェクトからこれまでに見た中で最も短く、最もまっすぐな答えの1つで、これはここで説明されています。
https://sourceware.org/bugzilla/show_bug.cgi?id=9939
ElectricFence、valgrind、dmallocなどの代替手法を検討する必要があります。
したがって、「malloc()関数とfree()関数はスレッドセーフですか」という意味であれば、答えは「はい」です。しかし、「malloc / freeスイート全体がスレッドセーフである」という意味であれば、答えはNOです。
簡単な答え:はい、スレッドの概念を含むC標準の最初のバージョンであるC11の時点で、malloc
友人はスレッドセーフである必要があります。スレッドとCランタイムの両方を含む多くのオペレーティングシステムは、C標準よりもずっと前にこの保証を行いましたが、私はすべてに誓う準備ができていません。ただし、malloc
友人は再入可能である必要はなく、またこれまでもそうする必要はありませんでした。
つまり、メモリ割り当ての他のルールに違反していない限り、複数のスレッドから同時に呼び出しmalloc
たり、ロックしたりする必要はありません(たとえば、によって返される各ポインタに対して1回だけ呼び出す)。ただし、シグナルを処理するスレッドへの呼び出しまたはスレッド内での呼び出しを中断した可能性のあるシグナルハンドラーからこれらの関数を呼び出すことは安全ではありません。場合によっては、ISO C以外の機能を使用して、シグナルを処理するスレッドがまたはへの呼び出しを中断しないことを保証できますが、他に選択肢がない場合を除いて、完全に正しくするのは難しいため、そうしないようにしてください。free
free
malloc
malloc
free
malloc
free
sigprocmask
sigpause
引用による長い答え:C標準は、2011年の改訂でスレッドの概念を追加しました(リンクは、無料で公開されている2011年標準の公式テキストに最も近いドキュメントN1570です)。その改訂では、セクション7.1.4のパラグラフ5は次のように述べています。
以下の詳細な説明で特に明記されていない限り、ライブラリ関数は、次のようにデータの競合を防止します。ライブラリ関数は、オブジェクトが関数の引数を介して直接または間接的にアクセスされない限り、現在のスレッド以外のスレッドによってアクセス可能なオブジェクトに直接または間接的にアクセスしてはなりません。 。ライブラリ関数は、オブジェクトが関数のnon-const引数を介して直接または間接的にアクセスされない限り、現在のスレッド以外のスレッドによってアクセス可能なオブジェクトを直接または間接的に変更してはなりません。オブジェクトがユーザーに表示されず、データの競合から保護されている場合、実装はスレッド間で独自の内部オブジェクトを共有する場合があります。
[脚注:これは、たとえば、スレッド間でオブジェクトを明示的に共有しないプログラムでもデータ競合が発生する可能性があるため、実装が同期なしで内部目的で静的オブジェクトを使用することを許可されていないことを意味します。同様に、memcpyの実装では、宛先オブジェクトの指定された長さを超えるバイトをコピーしてから元の値を復元することは許可されていません。これは、プログラムがスレッド間でそれらのバイトを共有した場合にデータ競合が発生する可能性があるためです。]
私が理解しているように、これは、C標準で定義されたライブラリ関数がスレッドセーフである必要があるという長い言い方です(通常の意味では、自分でロックすることなく、複数のスレッドから同時に呼び出すことができます)。 、特定の関数のドキュメントに特にそうではないと記載されていない限り、引数として渡されたデータと衝突しない限り)。
次に、7.22.3p2は、malloc、calloc、realloc、aligned_alloc、および特にfreeがスレッドセーフであることを確認します。
データ競合の存在を判断するために、メモリ割り当て関数は、引数を介してアクセス可能なメモリ位置のみにアクセスし、他の静的期間ストレージにはアクセスしないかのように動作します。ただし、これらの関数は、割り当てまたは割り当て解除するストレージを視覚的に変更する場合があります。メモリの領域pの割り当てを解除するfreeまたはreallocの呼び出しは、領域pのすべてまたは一部を割り当てる割り当て呼び出しと同期します。この同期は、割り当て解除機能によるpへのアクセスの後、割り当て機能によるそのようなアクセスの前に発生します。
7.24.5.8p6で、スレッドセーフではない、またはスレッドセーフではなかったstrtokについての説明と対比してください。
strtok関数は、strtok関数への他の呼び出しとのデータ競合を回避するために必要ではありません。
[脚注:代わりにstrtok_s関数を使用して、データの競合を回避できます。]
(脚注へのコメント:使用しないでくださいstrtok_s
、使用してstrsep
ください。)
古いバージョンのC標準は、スレッドセーフについて何も述べていません。ただし、信号は常にC標準の一部であるため、彼らは再入可能性について何かを言いました。そして、これは彼らが言ったことであり、元の1989 ANSI C標準に戻ります(このドキュメントは、翌年に発表されたISO C標準とほぼ同じ表現ですが、セクション番号が大きく異なります)。
アボートまたはレイズ関数を呼び出した結果以外の結果として[a]シグナルが発生した場合、シグナルハンドラーがシグナル関数自体以外の標準ライブラリ内の関数を呼び出すか、静的ストレージ期間が他のオブジェクトを参照する場合、動作は定義されません。タイプvolatilesig_atomic_tの静的ストレージ期間変数に値を割り当てるよりも。さらに、そのようなシグナル関数の呼び出しによってSIG_ERRが返される場合、errnoの値は不確定です。
これは、Cライブラリ関数は原則として再入可能である必要はないという長い言い方です。非常によく似た表現がC11、7.14.1.1p5にも表示されます:
アボートまたはレイズ関数を呼び出した結果以外の結果として[a]シグナルが発生した場合、シグナルハンドラーが、割り当て以外の方法でロックフリーのアトミックオブジェクトではない静的またはスレッドストレージ期間を持つオブジェクトを参照すると、動作は定義されません。 volatile sig_atomic_tとして宣言されたオブジェクトへの値、またはシグナルハンドラーが、abort関数、_Exit関数、quick_exit関数、または最初の引数が対応するシグナル番号に等しいシグナル関数以外の標準ライブラリ内の関数を呼び出すハンドラーの呼び出しを引き起こしたシグナル。さらに、そのようなシグナル関数の呼び出しによってSIG_ERRが返される場合、errnoの値は不確定です。
[脚注:非同期シグナルハンドラーによってシグナルが生成された場合、動作は定義されていません。]
POSIXは、Cライブラリの全体的なサイズと比較して、はるかに長いが、それでも短い必要があります。「非同期信号ハンドラ」から安全に呼び出すことができる関数のリストであり、信号が「他の」で発生する可能性のある状況をより詳細に定義します。アボートまたはレイズ関数を呼び出した結果よりも。」シグナルで重要なことをしている場合は、Unixの性質を持つOSで実行することを目的としたコードを書いている可能性があります(Windows、MVS、またはCの完全なホスト実装がない埋め込みのものとは対照的です)そもそも)、それらのPOSIX要件とISOC要件をよく理解しておく必要があります。
私は読むことをお勧めします
§31.1スレッドセーフ(および再入可能性の再検討)
『The Linux Programming Interface 』の中で、スレッドセーフと再入可能性の違いについて説明していますmalloc
。
抜粋:</ p>
関数は、複数のスレッドから同時に安全に呼び出すことができる場合、スレッドセーフであると言われます。逆に言えば、関数がスレッドセーフでない場合、別のスレッドで実行されている間、あるスレッドからその関数を呼び出すことはできません。
....
この関数は、関数がスレッドセーフではない一般的な理由を示しています。すべてのスレッドで共有されるグローバル変数または静的変数を使用します。...
スレッドセーフを実装するためのクリティカルセクションの使用は、関数ごとのミューテックスの使用よりも大幅に改善されていますが、ミューテックスのロックとロック解除にはコストがかかるため、それでも多少非効率的です。リエントラント関数は、ミューテックスを使用せずにスレッドセーフを実現します。これは、グローバル変数と静的変数の使用を回避することによって行われます。...
ただし、すべての機能を再入可能にすることはできません。通常の理由は次のとおりです。
- その性質上、一部の関数はグローバルデータ構造にアクセスする必要があります。mallocライブラリの関数が良い例です。これらの関数は、ヒープ上の空きブロックのグローバルリンクリストを維持します。ライブラリの機能は、
malloc
ミューテックスを使用することでスレッドセーフになります。...。
間違いなく読む価値があります。
そして、あなたの質問に答えるために、malloc
スレッドセーフですが、再入可能ではありません。
使用しているCランタイムライブラリの実装によって異なります。たとえば、MSVCを使用している場合は、ビルドするライブラリのバージョンを指定できるコンパイラオプションがあります(つまり、トレッドセーフであるかどうかによってマルチスレッドをサポートするランタイムライブラリ)。
いいえ、スレッドセーフではありません。実際には、Cライブラリで使用可能な関数がある場合がmalloc_lock()
あります。malloc_unlock()
私はこれらがNewlibライブラリに存在することを知っています。これを使用して、ハードウェアでマルチスレッド化されているプロセッサのミューテックスを実装する必要がありました。
mallocとfreeは、どのメモリブロックが空いているかを記録する静的データ構造を使用するため、再入可能ではありません。その結果、メモリを割り当てたり解放したりするライブラリ関数は再入可能ではありません。
いいえそうではありません。