MALLOC(3)

名称
malloc, calloc, realloc, free, reallocf - 汎用のメモリ割り当てのための関数群

ライブラリ
libc

書式
<stdlib.h> void * malloc(size_t size) void * calloc(size_t number, size_t size) void * realloc(void *ptr, size_t size) void * reallocf(void *ptr, size_t size) void free(void *ptr) char * malloc_options;

解説
malloc() 関数は、 size バイトのメモリを割り当てます。 割り当てられた空間は (可能なポインタ強制の後で) あらゆるタイプのオブジェクトが保存できるように適切に調整されます。 その空間の長さが少なくとも pagesize バイトである場合 ( getpagesize(3) 参照)、返されるメモリはページ境界が調整されていることでしょう。 malloc() に失敗すると、 NULL ポインタが返されます。

通常 malloc() が返すメモリは、0 のバイトで初期化されては いない ことに注意してください。

calloc() 関数は、 number 個の、長さが size であるオブジェクトに空間を割り当てます。 この関数を呼び出した結果は、割り当てられたメモリが 0 のバイトに 明示的に初期化されていることを除けば、引数 “number * size” で malloc() を呼び出した結果と同じです。

realloc() 関数は、 ptr で参照される、以前に割り当てられたメモリのサイズを size バイトに変更します。 新しいサイズと古いサイズのうち、 小さい方のサイズまでのメモリ内容は変更されません。 新しいサイズの方が大きい場合、 新しく割り当てられた部分のメモリの値は未定義です。 要求されたメモリを割り当てることができなかった場合は NULL が返されますが、 ptr が参照するメモリは有効で変更されていません。 ptr が NULL である場合、 realloc() 関数は、指定したサイズの malloc() と同じ動きをします。

reallocf() 関数は、要求されたメモリを割り当てることができなかった場合に、 渡されたポインタを解放することを除けば、 realloc() 関数呼び出しと同じです。 これは FreeBSD に固有の API で、 realloc() の従来型のコーディングスタイルを用いると、 ライブラリの内部でメモリリークを引き起こすという 問題をなくすために設計されました。

free() 関数は、 ptr で参照される割り当て済みのメモリを 今後の割り当てのために使用できるようにします。 ptr が NULL である場合、何も実行されません。

調整
これらのメモリ割り当てルーチン群の 1 つが初めて呼び出されるときに、 この割り当て実行の動作に影響を与える さまざまなフラグがセットもしくはリセットされます。

/etc/malloc.conf というシンボリックリンクで参照されるファイルの「名称」、 環境変数 MALLOC_OPTIONS の値、 グローバル変数 malloc_options が指す文字列は、 この順序で 1 文字毎にフラグとして解釈されます。

ほとんどのフラグは 1 文字で、 大文字は動作が設定されたこと、つまりオンになっていることを表し、 小文字は動作が設定されていないこと、つまりオフになっていることを表します。

A   すべての警告 (理解できないフラグが設定されているという警告を除く) を 致命的エラーにします。 この場合、プロセスは abort(3) を呼び出します。

J   malloc(), realloc(), reallocf() に割り当てられる新規のメモリ、同様に free(), realloc(), reallocf() で返されるメモリの各バイトを 0xd0 に初期化します。 このオプションは “R” オプションもセットします。 これはデバッグ用のオプションで、パフォーマンスの低下に強い影響を与えます。

H   割り当て関数で使用されてないページに関するヒントをカーネルに与えます。 システムが過度にページングを行っている場合は、パフォーマンスが向上します。 このオプションは、デフォルトでオフになっています。

R   最初の割り当てが十分に大きい場合であっても、 realloc() 関数と reallocf() 関数が常にメモリの再割り当てをするようになります。 メモリを圧縮する場合の大きな助けとなります。

U   すべてのオペレーションで、 ktrace(1) のための “utrace” エントリを生成します。 このオプションの詳細については、ソースを参照してください。

V   0 バイトを割り当てようとしたときに、 有効なポインタの代わりに NULL ポインタが返されるようになります (デフォルトの動作では、最小の割り当てを行って、そのポインタを返します)。 このオプションは System V との互換性のために提供されています。 このオプションは “X” オプションと適合しません。

X   割り当て関数でエラーを返す代わりに、 診断メッセージを stderr に 表示し、 ( abort(3) を使って) プログラムを core に落します。 このオプションは、コンパイル時に、 ソースコードに以下を組み込むようにして設定するべきです。

        extern char *malloc_options;
        malloc_options = "X";

Z   このオプションを設定すると、 “J” オプションと “R” オプションが 設定され、要求されたバイト列に 0 が出力されます。 これはデバッグ用のオプションで、パフォーマンスの低下に強い影響を与えます。

<   キャッシュサイズを 2 分の 1 にします。 デフォルトのキャッシュサイズは 16 ページです。 このオプションは複数回指定できます。

>   キャッシュサイズを 2 倍にします。 デフォルトのキャッシュサイズは 16 ページです。 このオプションは複数回指定できます。

“J” オプションと “Z” オプションは、テストとデバッグ用です。 これらのオプションを使用しているときに 動作が変わるアプリケーションには欠陥があります。

使用例
キャッシュサイズをシステム全体で小さくし、 問題が発生した場合は常にコアダンプを取るような設定は、

    ln -s 'A<' /etc/malloc.conf

プログラムがこれらの関数の呼出し時に戻り値をチェックしないことを ソースで明示するには、

    extern char *malloc_options;
    malloc_options = "X";

環境変数
以下の環境変数は、メモリ割り当て関数の実行に影響を与えます。

MALLOC_OPTIONS
    環境変数 MALLOC_OPTIONS を設定すると、この環境変数に含まれる文字は、 メモリ割り当て関数群のフラグとして解釈されます。

戻り値
malloc() 関数と calloc() 関数は、成功した場合は割り当てられたメモリへのポインタを返し、 それ以外の場合は NULL ポインタを返し errno を ENOMEM に設定します。

realloc() 関数と reallocf() 関数は、成功した場合はもしかしたら ptr と同一の、割り当てられたメモリへのポインタを返します。 それ以外の場合は NULL ポインタを返します。 その場合でも ptr によって参照されるメモリは利用可能でそのまま残ります。 メモリの割り当てに失敗した場合は、 errno を ENOMEM に設定します。

free() 関数は値を返しません。

MALLOC の問題のデバッグ
この実装は、 割り当てられていない限り解放されているページはアクセスされず、 再利用のためにカーネルに積極的に返されるという点が、 他 (のシステム) のメモリ割り当ての実装と大きく異なります。

ほとんど (のシステム) のメモリ割り当ての実装では、 リンクドリストを含んだデータ構造が、 解放されているメモリのかたまりの中に保存され、 解放されているすべてのメモリを互いに結合するために使用されます。 解放リストを行き来するたびに、 未使用の、ページアウトされているであろうページが、 プライマリメモリに入るためにページフォルトを起こすため、 これは最適とは言えません。 ページングを行うシステムでは、 1 つのプロセスによって生じるページフォルトの数が 5 倍に増加する結果となることがあります。

このアーキテクチャには、これまでは検出されなかったインタフェースの 細々とした違反が、実際に検出されるようになるという副作用があります。 このため、ずっと問題なく動いていたプログラムが、この割り当ての実装と リンクしたとたんに問題が続出することがあります。

最初にするべきこと、そしてもっとも重要なことは “A” オプションを設定することです。 このオプションを設定すると、 できるだけ処理を継続しようとする通常の方針をとるかわりに、 問題が発生したときに (可能であれば) コアダンプを強制的に取ります。

デバッガのサポートのために、 適切なオプションとシンボルでプログラムを再コンパイルするのが おそらく賢明です。

プログラムが、通常とは異なる結果を出したり、コアダンプしたり、 次のセクションで挙げるようなメッセージを出さずに 違った動作をし始めるような場合は、 プログラムが 0 のバイトで満たされている記憶領域に依存している場合だと 思われます。 “Z” オプションを設定して実行してみてください。 状況が好転した場合は、この診断が正しかったことになります。 これでもプログラムがおかしな動作をするようなら、 割り当てられた領域以外のメモリ、 大抵は割り当てられた領域の前方ではなく後方にアクセスするという問題だと 思われます。

あるいは、症状が容易に再現しない場合は、 “J” オプションを設定すると問題を起こす助けになるかもしれません。

本当に難しい状況では、カーネルで “U” オプションがサポートされている場合はそれを設定すると、 これらの関数のすべての呼出しの詳細なトレースが作成されます。

あいにく、この実装では、検出された問題に関する詳細は提供されません。 そのような情報を保存することで、パフォーマンスが悪影響を受けるためです。 パフォーマンスと引き換えにいっそうの健全さのチェックと詳細な診断を行い、 問題の検出と位置の特定に焦点を当てた数多くのメモリ割り当ての実装が、 インターネットで利用可能です。

診断メッセージ
malloc(), calloc(), realloc(), free() がエラーや警告を検出すると、 メッセージがファイル記述子 STDERR_FILENO に出力されます。 エラーの場合、プロセスはコアダンプします。 “A” オプションを設定すると、すべての 警告はエラーとして扱われます。

以下では、出力される可能性があるエラーメッセージとその意味について 簡単に説明します。

(ES): mumble mumble mumble “EXTRA_SANITY” が定義された状態でメモリ割り当て関数がコンパイルされており、 詳細なエラーチェック中にエラーが検出されました。 詳細については、ソースコードを参照してください。

mmap(2) failed, check limits システムが危険な過負荷な状態であるか、プロセスの制限が正しく 指定されていないと思われます。

freelist is destroyed 内部の解放リストが壊れています。

out of memory “X” オプションが指定されていて、かつ、メモリの割り当てに失敗しました。

以下では、出力される可能性がある警告メッセージとその意味について 簡単に説明します。

chunk/page is already free すでに解放されているメモリを free() で解放しようとしました。

junk pointer, ... メモリ割り当て関数に与えられたポインタが、 認識されているメモリ境界の外側を指しています。

malloc() has never been called メモリが割り振られていないにも関わらず、解放しようとしたり、 再割り当てしようとしました。

modified (chunk-/page-) pointer free() か realloc() に渡されたポインタが書き換えられています。

pointer to wrong page realloc(), free() もしくは reallocf() が解放しようとしているポインタが、 正しいページを参照していません。

recursive call メモリ割り当て関数を再帰的に呼び出そうとしました。 これは許可されていません。 特にシグナルハンドラでは、メモリの割り当てをするべきではありません。

unknown char in MALLOC_OPTIONS 不明なオプションが指定されました。 “A” オプションを設定していても、この警告は単なる警告として扱われます。

関連項目
brk(2), mmap(2), alloca(3), getpagesize(3), memory(3) /usr/share/doc/papers/malloc.ascii.gz

規格
malloc(), calloc(), realloc(), free() 関数は isoC に適合しています。

歴史
現在のメモリ割り当ての実装は、 個別のゲルマニウムトランジスタで作成された、 20 ビットバイナリコンピュータに取り付けられたドラムの ファイルシステムとしてスタートしました。 それ以降は、二次保存域ではなく、一次保存域を操作するようになりました。 この新しい形態と機能は FreeBSD 2.2 ではじめて登場しました。

reallocf(3) 関数は FreeBSD 3.0 ではじめて登場しました。

作者
Poul-Henning Kamp <phk@FreeBSD.org>

バグ
問題が発生した場合に出力されるメッセージは、 実際の値についての詳細を提供しません。

0 バイトを割り当てるように要求された場合に NULL ポインタを返すことは、愚かな問いかけに対する愚かな反応であると言えます。