web-dev-qa-db-ja.com

mallocはスレッドセーフですか?

malloc()関数は再入可能ですか?

70
Alphaneo

-pthreadを指定してコンパイルすると、mallocがスレッドセーフになります。ただし、mallocはANSI Cであり、スレッドはそうではないため、その実装に依存していると確信しています。

Gccについて話している場合:

-pthreadおよびmalloc()を使用してコンパイルおよびリンクすると、x86およびAMD64でスレッドセーフになります。

http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2431a99b9bdcef11/ea800579e40f7fa4

別の意見、より洞察力のある

glibc-2.2 +の{malloc、calloc、realloc、free、posix_memalign}はスレッドセーフです

http://linux.derkeiler.com/Newsgroups/comp.os.linux.development.apps/2005-07/0323.html

42
Tom

質問:「mallocは再入可能ですか?」
回答:いいえ、違います。ルーチンを作成するものの1つの定義 リエントラント です。

Mallocの一般的なバージョンでは、(たとえば、シグナルハンドラから)再入力することはできません。再入可能ルーチンはロックを使用しない場合があり、既存のほとんどすべてのmallocバージョンはロック(スレッドセーフにする)またはグローバル/静的変数(スレッドセーフにする)を使用することに注意してくださいand非リエントラント)。

これまでのすべての答えは「malloc thread-safe?」と答えていますが、これはまったく別の質問です。 that質問への答えはそれはランタイムライブラリに依存します、そしておそらくコンパイラのフラグを使用します。最新のUNIXでは、デフォルトでスレッドセーフなmallocを取得します。 Windowsでは、/MT/MTd/MD、または/MDdフラグを使用して、スレッドセーフランタイムライブラリを取得します。

96

これはかなり古い質問であり、現状に応じて新鮮さをもたらしたいと思います。

はい、現在malloc()はスレッドセーフです。

GNU Cライブラリリファレンスマニュアル of _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では、マルチスレッドの略です。

MT-Safeであることは、関数がアトミックであることを意味するものでも、POSIXがユーザーに公開するメモリ同期メカニズムを使用することでもありません。 MT-Safe関数を順番に呼び出しても、MT-Safeの組み合わせが得られない可能性があります。たとえば、スレッドが2つのMT-Safe関数を順番に呼び出すことは、他のスレッドでの同時呼び出しが破壊的な方法で干渉する可能性があるため、両方の関数の組み合わせのアトミック実行と同等の動作を保証しません。

ライブラリインターフェイス全体で関数をインライン化できるプログラム全体の最適化により、安全でない並べ替えが公開される可能性があるため、GNU Cライブラリインターフェイスでインライン化を実行することは推奨されません。ただし、ユーザーに表示されるヘッダーで定義された関数は、インライン化しても安全であるように設計されています。

9
likern

Glibcのmalloc.cからの抜粋を次に示します。

スレッドセーフ:NO_THREADSが定義されていない限り、スレッドセーフ

nO_THREADSがデフォルトで定義されていないと仮定すると、少なくともLinuxではmallocはスレッドセーフです。

9
shahkhas

はい、 POSIX.1-2008mallocの下はスレッドセーフです。

2.9.1スレッドセーフ

このボリュームのPOSIX.1-2008で定義されているすべての関数はスレッドセーフでなければなりません。ただし、次の関数1はスレッドセーフである必要はありません。

[mallocを含まない関数のリスト]

6
Tanz87

GLIBCを使用している場合、答えは「はい、しかし」です。

特に、はい、しかし、mallocとfreeはスレッドセーフですが、デバッグ関数はそうではないことに注意してください

特に、非常に便利なmtrace()、mcheck()、およびmprobe()関数はスレッドセーフではありません。 GNUプロジェクトから得られる最短かつ真っ直ぐな回答の1つで、これについて説明します。

https://sourceware.org/bugzilla/show_bug.cgi?id=9939

ElectricFence、valgrind、dmallocなどの代替手法を検討する必要があります。

したがって、「malloc()関数とfree()関数はスレッドセーフですか」という意味であれば、答えはイエスです。しかし、「malloc/freesuiteスレッドセーフ全体」という意味であれば、答えは「いいえ」です。

5
breakpoint

簡単な答え:はい、C11の時点で、スレッドの概念を含むC標準の最初のバージョンであり、mallocおよびフレンドはスレッドセーフである必要があります。スレッドとCランタイムの両方を含む多くのオペレーティングシステムは、C標準よりもずっと前にこの保証を行いましたが、allに誓う準備はできていません。ただし、mallocおよび友人は、再入可能にする必要はありません。

つまり、mallocおよびfreeを複数のスレッドから同時に呼び出しても、メモリ割り当ての他の規則を破らない限り(たとえば、freemallocによって返される各ポインターで1回だけ。ただし、シグナルを処理するスレッドでmallocまたはfreeへの呼び出しを中断した可能性のあるシグナルハンドラーからこれらの関数を呼び出すことはnot安全です。 ISO Cを超える機能を使用すると、信号を処理するスレッドがmallocまたはfreeへの呼び出しを中断させなかったことを保証できる場合があります。 sigprocmasksigpauseを使用しますが、他に選択肢がない場合は、そうしないようにしてください。完全に正しくなるのは難しいからです。


引用付きの長い答え:C標準は、スレッドの概念を 2011リビジョン に追加しました(リンクは文書N1570です。これは、2011年標準の公式テキストに最も近いもので、公開されていません。電荷)。その改訂では、- セクション7.1.4パラグラフ5 は次のように述べています。

以下の詳細な説明で明示的に明記されていない限り、ライブラリ関数は、次のようにデータの競合を防止するものとします:ライブラリ関数は、オブジェクトが関数の引数を介して直接または間接的にアクセスされない限り、現在のスレッド以外のスレッドによってアクセス可能なオブジェクトに直接または間接的にアクセスしてはなりません。ライブラリ関数は、関数の非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規格の古いバージョンは、スレッドセーフについては何も述べていません。ただし、didはリエントラントについて何かを言います。信号は常にC標準の一部であるためです。そして、これは彼らが言ったことであり、元に戻ります 1989 ANSI C標準 (この文書は、翌年に出されたISO C標準とはほぼ同一の文言ですが、セクション番号は非常に異なります) ):

[a]シグナルがアボートまたはレイズ関数の呼び出しの結果として以外に発生した場合、シグナルハンドラーがシグナル関数自体以外の標準ライブラリ内の関数を呼び出すか、または静的ストレージ期間を持つオブジェクトを参照する場合の動作は未定義ですタイプvolatile sig_atomic_tの静的ストレージ期間変数に値を割り当てるよりも。さらに、そのようなシグナル関数の呼び出しがSIG_ERRを返す場合、errnoの値は不定です。

これは、Cライブラリ関数は一般的な規則としてリエントラントである必要があるnotであると言っている長い言い方です。 C11にも非常によく似た表現が表示されます 7.14.1.1p5

[a]シグナルがアボートまたはレイズ関数の呼び出しの結果として以外に発生した場合、シグナルハンドラが静的またはスレッドストレージ期間を持つオブジェクトを参照する場合の動作は未定義です。 volatile sig_atomic_tとして宣言されたオブジェクトの値、またはシグナルハンドラーが、アボート関数、_Exit関数、quick_exit関数、または対応するシグナル番号に等しい最初の引数を持つシグナル関数以外の標準ライブラリ内の関数を呼び出すハンドラーの呼び出しを引き起こしたシグナル。さらに、そのようなシグナル関数の呼び出しがSIG_ERRを返す場合、errnoの値は不定です。

[脚注:非同期シグナルハンドラーによってシグナルが生成された場合、動作は未定義です。]

POSIXには、 Cライブラリの全体サイズに比べてはるかに長いが、それでも短い 、「非同期シグナルハンドラ」から安全に呼び出し可能な関数のリストが必要です。シグナルは「アボートまたはレイズ機能の呼び出しの結果として以外に発生する」可能性があります。シグナルで自明ではないことをしている場合は、おそらく、Unixの性質を持つOS(Windows、MVS、またはCの完全なホストされた実装を持たない組み込みのものとは対照的に)で実行することを目的としたコードを書いているでしょうそもそも)、それらのPOSIX要件とISO C要件をよく理解する必要があります。

2
zwol

いいえ、スレッドセーフではありません。実際には、Cライブラリでmalloc_lock()およびmalloc_unlock()関数を使用できる場合があります。これらがNewlibライブラリに存在することは知っています。これを使用して、ハードウェアでマルチスレッド化されたプロセッサのミューテックスを実装する必要がありました。

1
sybreon

mallocとfreeは、どのメモリブロックが解放されているかを記録する静的データ構造を使用するため、リエントラントではありません。その結果、メモリを割り当てたり解放したりするライブラリ関数はリエントラントではありません。

1
user3379688

使用しているCランタイムライブラリの実装によって異なります。たとえば、MSVCを使用している場合は、ビルドするライブラリのバージョンを指定できるコンパイラオプションがあります(つまり、トレッドセーフであるかどうかによってマルチスレッドをサポートするランタイムライブラリ)。

1
ChrisW
0
Paul Sonier