75

Linux では、malloc(1024 * 1024 * 1024)malloc は実際に何をするのでしょうか?

仮想アドレスを割り当てに割り当てていると確信していますが (必要に応じて空きリストを調べて新しいマッピングを作成することにより)、実際に 1 GiB 相当のスワップ ページを作成しますか? それともmprotect、実際に触れたときにアドレス範囲とページを作成しますmmapか?

(Linux を指定しているのは、標準がこの種の詳細について沈黙しているからですが、他のプラットフォームも同様に何をしているのか知りたいです。)

4

6 に答える 6

43

Linux は遅延ページ割り当てを行います。「楽観的なメモリ割り当て」。malloc から返されるメモリは何にも支えられておらず、それに触れると実際に OOM 状態になる場合があります (要求したページのスワップ領域がない場合)。この場合、プロセスは突然終了します

たとえば、http://www.linuxdevcenter.com/pub/a/linux/2006/11/30/linux-out-of-memory.htmlを参照してください。

于 2009-05-26T17:50:18.050 に答える
16

9. メモリー( The Linux kernel一部、Andries Brouwer によるLinux Kernel に関するコメント) は、優れたドキュメントです。

これには、Linux の物理メモリと実際のメモリの処理を示し、カーネルの内部構造を説明する次のプログラムが含まれています。

通常、最初のデモ プログラムは、malloc() が NULL を返す前に大量のメモリを取得します。2 番目のデモ プログラムは、以前に取得したメモリが実際に使用されるため、はるかに少ない量のメモリを取得します。3 番目のプログラムは、最初のプログラムと同じ量を取得し、メモリを使用する必要があるときに強制終了されます。

デモ プログラム 1: メモリを使用せずに割り当てます。

#include <stdio.h>
#include <stdlib.h>

int main (void) {
    int n = 0;

    while (1) {
        if (malloc(1<<20) == NULL) {
                printf("malloc failure after %d MiB\n", n);
                return 0;
        }
        printf ("got %d MiB\n", ++n);
    }
}

デモ プログラム 2: メモリを割り当てて、実際にすべてに触れます。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (void) {
    int n = 0;
    char *p;

    while (1) {
        if ((p = malloc(1<<20)) == NULL) {
                printf("malloc failure after %d MiB\n", n);
                return 0;
        }
        memset (p, 0, (1<<20));
        printf ("got %d MiB\n", ++n);
    }
}

デモ プログラム 3: 最初に割り当て、後で使用します。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define N       10000

int main (void) {
    int i, n = 0;
    char *pp[N];

    for (n = 0; n < N; n++) {
        pp[n] = malloc(1<<20);
        if (pp[n] == NULL)
            break;
    }
    printf("malloc failure after %d MiB\n", n);

    for (i = 0; i < n; i++) {
        memset (pp[i], 0, (1<<20));
        printf("%d\n", i+1);
    }

    return 0;
}

( Solarisのような正常に機能するシステムでは、3 つのデモ プログラムは同じ量のメモリを取得し、クラッシュしませんが、malloc() が NULL を返すことを確認してください。)

于 2009-05-26T17:49:07.210 に答える
11

同じ件名の同様の投稿にこの回答を与えました。

一部のアロケーターは怠惰ですか?

これは少し本題から外れますが (それからあなたの質問に結び付けます)、何が起こっているかは、Linux でプロセスをフォークしたときに起こることと似ています。フォークする場合、コピーオンライトと呼ばれるメカニズムがあり、メモリが書き込まれるときに新しいプロセスのメモリスペースのみをコピーします。このようにして、フォークされたプロセス exec がすぐに新しいプログラムを実行する場合、元のプログラムのメモリをコピーするオーバーヘッドを節約できます。

質問に戻りますが、考え方は似ています。他の人が指摘しているように、メモリを要求すると仮想メモリ空​​間がすぐに取得されますが、実際のページはそれらに書き込むときにのみ割り当てられます。

これの目的は何ですか?基本的に、メモリの割り当てを Big O(n) 操作ではなく、多かれ少なかれ一定時間の操作 Big O(1) にします (Linux スケジューラが 1 つの大きなチャンクで実行するのではなく、それを分散させる方法に似ています)。

私が何を意味するかを実証するために、次の実験を行いました。

rbarnes@rbarnes-desktop:~/test_code$ time ./bigmalloc

real    0m0.005s
user    0m0.000s
sys 0m0.004s
rbarnes@rbarnes-desktop:~/test_code$ time ./deadbeef

real    0m0.558s
user    0m0.000s
sys 0m0.492s
rbarnes@rbarnes-desktop:~/test_code$ time ./justwrites

real    0m0.006s
user    0m0.000s
sys 0m0.008s

bigmalloc プログラムは 2000 万の int を割り当てますが、何もしません。デッドビーフは各ページに 1 つの int を書き込み、その結果 19531 回の書き込みが発生し、justwrites は 19531 個の int を割り当ててそれらをゼロにします。ご覧のとおり、deadbeef の実行には bigmalloc の約 100 倍、justwrites の約 50 倍の時間がかかります。

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = malloc(sizeof(int)*20000000); // Allocate 80 million bytes

    return 0;
}

.

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = malloc(sizeof(int)*20000000); // Allocate 80 million bytes

    // Immediately write to each page to simulate an all-at-once allocation
    // assuming 4k page size on a 32-bit machine.

    for (int* end = big + 20000000; big < end; big += 1024)
        *big = 0xDEADBEEF;

    return 0;
}

.

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = calloc(sizeof(int), 19531); // Number of writes

    return 0;
}
于 2009-05-26T19:16:00.857 に答える
6

malloc は、libc が管理するブロックからメモリを割り当てます。追加のメモリが必要な場合、ライブラリは brk システム コールを使用してカーネルにアクセスします。

カーネルは、仮想メモリのページを呼び出しプロセスに割り当てます。ページは、プロセスが所有するリソースの一部として管理されます。メモリがブレークされた場合、物理ページは割り当てられません。プロセスが brk'd ページのいずれかのメモリ位置にアクセスすると、ページ フォールトが発生します。カーネルは、仮想メモリが割り当てられていることを確認し、物理ページを仮想ページにマップします。

ページ割り当ては書き込みに限定されず、コピー オン ライトとはまったく異なります。読み取りまたは書き込みのすべてのアクセスにより、ページ フォールトと物理ページのマッピングが発生します。

スタック メモリは自動的にマップされることに注意してください。つまり、スタックによって使用される仮想メモリにページをマップするために、明示的な brk は必要ありません。

于 2009-05-27T01:20:25.707 に答える
5

Windows では、ページはコミットされます (つまり、使用可能な空きメモリが減少します) が、実際には、ページにアクセスする (読み取りまたは書き込み) まで割り当てられません。

于 2009-05-26T18:07:51.713 に答える
2

ほとんどの Unix 系システムでは、brk境界を管理します。VM は、プロセッサによってヒットされるとページを追加します。少なくとも Linux とBSDはこれを行います。

于 2009-05-26T17:49:23.480 に答える