malloc()のヒープの拡大を確認する

malloc()の実装は(細かい部分は無視して)
・OSからヒープをまとめて取得する。
malloc()されるとヒープを分けあたえる。
・ヒープに余裕がない場合は、ヒープを拡大する。
と言う感じだと思います。


でこの流れを実感できる方法があったのでメモ。

/proc/[pid]/mapsはpid番号のプロセスに割り当てられたメモリ領域です。
試しにここをのぞいてみます。
ソースはこんな感じ。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(void){
        char cmd[256];

        snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid());
        system(cmd);

        return 0;
}


環境はこんな感じ。

$ cat /proc/version
Linux version 2.6.20-16-generic (root@terranova) (gcc version 4.1.2 (Ubuntu 4.1.2-0ubuntu4)) #2 SMP Thu Jun 7 20:19:32 UTC 2007


実行するとこんな感じ。
*見て分かると思いますが、08048000-08049000はアドレスの範囲です。

$ ./a.out 
08048000-08049000 r-xp 00000000 08:02 481863     /home/****/src/c/proc_map/a.out
08049000-0804a000 rw-p 00000000 08:02 481863     /home/****/src/c/proc_map/a.out
b7dc0000-b7dc1000 rw-p b7dc0000 00:00 0 
b7dc1000-b7efc000 r-xp 00000000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7efc000-b7efd000 r--p 0013b000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7efd000-b7eff000 rw-p 0013c000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7eff000-b7f02000 rw-p b7eff000 00:00 0 
b7f0f000-b7f11000 rw-p b7f0f000 00:00 0 
b7f11000-b7f2a000 r-xp 00000000 08:02 6224       /lib/ld-2.5.so
b7f2a000-b7f2c000 rw-p 00019000 08:02 6224       /lib/ld-2.5.so
bfa36000-bfa4b000 rw-p bfa36000 00:00 0          [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0          [vdso]


次にmalloc()を行ってみる。
*テストなので、エラー処理やfree()はしていません。

1$ cat main.c 
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>


int main(void){
        char cmd[256];
        char *p;

        p = malloc(0x100);

        snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid());
        system(cmd);

        return 0;
}


これを実行してみる

$ ./a.out 
08048000-08049000 r-xp 00000000 08:02 481863     /home/****/src/c/proc_map/a.out
08049000-0804a000 rw-p 00000000 08:02 481863     /home/****/src/c/proc_map/a.out
0804a000-0806b000 rw-p 0804a000 00:00 0          [heap]
b7da2000-b7da3000 rw-p b7da2000 00:00 0 
b7da3000-b7ede000 r-xp 00000000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7ede000-b7edf000 r--p 0013b000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7edf000-b7ee1000 rw-p 0013c000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7ee1000-b7ee4000 rw-p b7ee1000 00:00 0 
b7ef1000-b7ef3000 rw-p b7ef1000 00:00 0 
b7ef3000-b7f0c000 r-xp 00000000 08:02 6224       /lib/ld-2.5.so
b7f0c000-b7f0e000 rw-p 00019000 08:02 6224       /lib/ld-2.5.so
bfbb1000-bfbc6000 rw-p bfbb1000 00:00 0          [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0          [vdso]


注目するところはここ。
[heap]というのが増えているのが分かる。

0804a000-0806b000 rw-p 0804a000 00:00 0          [heap]

ここが、「・OSからヒープをまとめて取得する。」という部分。


さらにmalloc()をしてみる。

1$ cat main.c 
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>


int main(void){
        char cmd[256];
        char *p;
        int i;

        for(i=0;i<1000;i++){
                p = malloc(0x100);
        }

        snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid());
        system(cmd);

        return 0;
}


実行してみると分かるが[heap]の領域が増えているのが分かる。

$ ./a.out 
08048000-08049000 r-xp 00000000 08:02 481867     /home/yasusi/src/c/proc_map/a.out
08049000-0804a000 rw-p 00000000 08:02 481867     /home/yasusi/src/c/proc_map/a.out
0804a000-0808c000 rw-p 0804a000 00:00 0          [heap]
b7e26000-b7e27000 rw-p b7e26000 00:00 0 
b7e27000-b7f62000 r-xp 00000000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7f62000-b7f63000 r--p 0013b000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7f63000-b7f65000 rw-p 0013c000 08:02 25028      /lib/tls/i686/cmov/libc-2.5.so
b7f65000-b7f68000 rw-p b7f65000 00:00 0 
b7f75000-b7f77000 rw-p b7f75000 00:00 0 
b7f77000-b7f90000 r-xp 00000000 08:02 6224       /lib/ld-2.5.so
b7f90000-b7f92000 rw-p 00019000 08:02 6224       /lib/ld-2.5.so
bff58000-bff6d000 rw-p bff58000 00:00 0          [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0          [vdso]


ちなみに、r-xpは書き込み禁止、rw-pは書き込めることを示しています。
つまり、

char *p = "hoge";
int i;

上記の"p"はr-xp、"i"はrw-pになります。
r-xpに書き込みを行うとSegmentation faultになりますが、
割り当てられていなくても、rw-pに書き込みは可能です。
もちろんmapsにないエリアに書き込みを行ってもSegmentation faultになります。


ポインタ関係のバグで、Segmentation faultになったり、何事もなく処理が進んだりするのはこのあたりが関係しています。