私はこの単純なCプログラムを持っています:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main (int argc, char **argv) {
int i = 0;
int j = 0;
size_t size = 4194304; /* 4 MiB */
char *buffer = malloc(size);
char *buffers[10] = {NULL};
void *tmp_pointer = NULL;
fprintf(stderr, "initial size == %zu\n", size);
fprintf(stderr, "initial buffer == %p\n\n", buffer);
srand(time(NULL));
/* let's see when it fails ... */
for (; i < 10; ++i) {
/* some random writes */
for (j = 0; j < 1000; ++j) {
buffer[rand() % size] = (char)(rand());
}
/* some interleaving memory allocations */
buffers[i] = malloc(1048576); /* 1 MiB */
size *= 2;
fprintf(stderr, "new size == %zu\n", size);
tmp_pointer = realloc(buffer, size);
if ((tmp_pointer == NULL) || (errno != 0)) {
fprintf(stderr, "tmp_pointer == %p\n", tmp_pointer);
fprintf(stderr, "errno == %d\n", errno);
perror("realloc");
return (1);
} else {
buffer = tmp_pointer;
}
fprintf(stderr, "new buffer == %p\n\n", buffer);
}
fprintf(stderr, "Trying to free the buffers.\n");
free(buffer);
if (errno != 0) {
fprintf(stderr, "errno == %d\n", errno);
perror("free(buffer)");
return (2);
}
for (i = 0; i < 10; ++i) {
free(buffers[i]);
if (errno != 0) {
fprintf(stderr, "i == %d\n", i);
fprintf(stderr, "errno == %d\n", errno);
perror("free(buffers)");
return (3);
}
}
fprintf(stderr, "Successfully freed.\n");
return (0);
}
4 MiB のメモリを割り当てるだけで、再割り当てによってそのサイズを 2 倍にしようと 10 回試みます。realloc への単純な呼び出しは、ヒープ アロケーターの「トリック」を最小限に抑えるために、1 MiB ブロックの別の割り当てといくつかのランダム書き込みでインターリーブされます。16 GiB RAM を搭載した Ubuntu Linux マシンでは、次の出力が得られます。
./realloc_test
initial size == 4194304
initial buffer == 0x7f3604c81010
new size == 8388608
new buffer == 0x7f3604480010
new size == 16777216
new buffer == 0x7f360347f010
new size == 33554432
new buffer == 0x7f360147e010
new size == 67108864
new buffer == 0x7f35fd47d010
new size == 134217728
new buffer == 0x7f35f547c010
new size == 268435456
new buffer == 0x7f35e547b010
new size == 536870912
new buffer == 0x7f35c547a010
new size == 1073741824
new buffer == 0x7f3585479010
new size == 2147483648
new buffer == 0x7f3505478010
new size == 4294967296
new buffer == 0x7f3405477010
Trying to free the buffers.
Successfully freed.
そのため、4 GiB までのすべての再割り当ては成功しているように見えます。ただし、次のコマンドでカーネル仮想メモリのアカウンティング モードを 2 (常にチェックし、オーバーコミットしない) に設定すると、次のようになります。
echo 2 > /proc/sys/vm/overcommit_memory
出力は次のように変わります。
./realloc_test
initial size == 4194304
initial buffer == 0x7fade1fa7010
new size == 8388608
new buffer == 0x7fade17a6010
new size == 16777216
new buffer == 0x7fade07a5010
new size == 33554432
new buffer == 0x7fadde7a4010
new size == 67108864
new buffer == 0x7fadda7a3010
new size == 134217728
new buffer == 0x7fadd27a2010
new size == 268435456
new buffer == 0x7fadc27a1010
new size == 536870912
new buffer == 0x7fada27a0010
new size == 1073741824
new buffer == 0x7fad6279f010
new size == 2147483648
tmp_pointer == (nil)
errno == 12
realloc: Cannot allocate memory
再配置は 2 GiB で失敗します。によって報告された時点でのコンピューターの空きメモリはtop約 5 GiB であり、realloc は常にメモリの連続ブロックを割り当てる必要があるため、妥当です。ここで、同じマシンの VirtualBox 内で Mac OS X Lion で同じプログラムを実行するとどうなるか見てみましょう。仮想 RAM は 8 GiB しかありません。
./realloc_test
initial size == 4194304
initial buffer == 0x101c00000
new size == 8388608
tmp_pointer == 0x102100000
errno == 22
realloc: Invalid argument
ここで、このプログラムには、最初の 8 MiB への再割り当てに問題があります。私の意見では、これは非常に奇妙topです.
しかし、実際には realloc が成功したのは、その戻り値が非 NULL であるためです (tmp_pointerプログラム終了の直前の値に注意してください)。しかし、まったく同じように realloc の呼び出しが成功すると、errno もゼロ以外に設定されます。さて、この状況を処理する正しい方法は何ですか?
errno を無視して、realloc からの戻り値のみをチェックする必要がありますか? しかし、次の errno ベースのエラー ハンドラはどうでしょうか。これはおそらく良い考えではありません。
realloc が非 NULL ポインターを返す場合、errno をゼロに設定する必要がありますか? これは解決策のようです。しかし...私はここを見ました: http://austingroupbugs.net/view.php?id=374。このリソースがどれほど権威があるかはわかりませんが、realloc に関しては、これについては非常に明確です。
「...標準では、文書化されていない限り、成功時にerrnoを検査できないことも明示されています...」
私の理解が正しければ、それは次のように言っています。つまり、errno をゼロにリセットできますか? 一度も見ずに?何が悪いのか、何が良いのかを理解して判断するのは非常に不明確だと思います。
そもそもなぜ realloc がこの errno を設定するのか、まだ理解できません。そして、「無効な引数」の値はどういう意味ですか? man ページには記載されておらず、errno ENOMEM (通常は 12 番) のみが記載されています。何か問題が発生する可能性がありますか?この単純なプログラムの何かが Mac OS X でこの動作を引き起こしているのでしょうか? おそらくそうでしょう...だから、2つの主な質問は次のとおりです。
- なにが問題ですか?と
- それを修正する方法は?より正確には、この単純なプログラムを改善して、Mac OS X の realloc が成功したときに errno をゼロのままにする方法は?