27.1. gdb で sbrk() の解析

sbrk() の振る舞いはソコソコ説明できたと思います。

なのでもう少し内部の突っ込んだ部分も検証したいですね。

それで検証としては gdb が適していると思います。

では gdb でプログラムの内部の検証もやってみましょう。

$ gdb ./a.out
(gdb) break main
Breakpoint 1 at 0x772: file main.cpp, line 9.
(gdb) run
Starting program: /home/komatsu/allocator/brk/a.out

Breakpoint 1, main () at main.cpp:9
9         std::printf("initial sbrk: 0x%lx\n",(unsigned long)sbrk(0));
(gdb) info proc mappings
process 19447
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555555000     0x1000        0x0 /home/komatsu/allocator/brk/a.out
      0x555555754000     0x555555755000     0x1000        0x0 /home/komatsu/allocator/brk/a.out
      0x555555755000     0x555555756000     0x1000     0x1000 /home/komatsu/allocator/brk/a.out

「info proc mappings」はメモリーレイアウトを表示してくれます。

注目すべきはこの時点でヒープらしき領域がメモリーレイアウトにないことですかね。

では sbrk() 関数を使うとメモリーレイアウトに変化がないか試してみたいと思います。

(gdb) n
initial sbrk: 0x555555756000

この「 0x555555756000 」は「 /home/komatsu/allocator/brk/a.out 」の終端アドレスと同じですね。

10        int *p = (int*)std::malloc(sizeof(int));
(gdb) info proc mappings
process 19447
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555555000     0x1000        0x0 /home/komatsu/allocator/brk/a.out
      0x555555754000     0x555555755000     0x1000        0x0 /home/komatsu/allocator/brk/a.out
      0x555555755000     0x555555756000     0x1000     0x1000 /home/komatsu/concurrent/cpp17_test/allocator/brk/a.out
      0x555555756000     0x555555777000    0x21000        0x0 [heap]
//以下、出力は割愛
(gdb) n
11        std::printf("malloc'd address: 0x%lx\n",(unsigned long)p);
(gdb) n
malloc'd address: 0x555555756670

以下が重要な行です。

      0x555555756000     0x555555777000    0x21000        0x0 [heap]

「 0x21000 」 の領域が確保されてますが 10 進数に直したサイズは「 135168 」バイトとなります。

この時点では malloc() は次に実行するコマンドなので malloc() での領域確保はコールしてません…

どうして… (´・ω・`)

まあ、これは cstdio ヘッダーまたは stdio.h ヘッダーの printf() 関数をコールした結果としてバックグランドでヒープにメモリーが確保されたんだと筆者は考えています。

ええ… たぶんそうです…

では今度は malloc() を明示的に呼び出してレイアウトをチェックしましょう。

12        std::printf("second sbrk: 0x%lx\n",(unsigned long)sbrk(0));
(gdb) n
second sbrk: 0x555555777000

このように「initial sbrk: 0x555555756000」から「second sbrk: 0x555555777000」というように動いてます。

malloc() は「 malloc’d address: 0x555555756670 」というようになり、アドレス域は近いですね。

13        int *w = (int*)std::malloc(sizeof(int)*8192*1024);
(gdb) n
14        if(w == nullptr)
(gdb) n
16        std::printf("malloc'd address: 0x%lx\n",(unsigned long)w);
(gdb) n
malloc'd address: 0x7ffff59e3010
17        std::printf("third sbrk: 0x%lx\n",(unsigned long)sbrk(0));
(gdb) info proc mapp
process 18340
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555555000     0x1000        0x0 /home/komatsu/allocator/brk/a.out
      0x555555754000     0x555555755000     0x1000        0x0 /home/komatsu/allocator/brk/a.out
      0x555555755000     0x555555756000     0x1000     0x1000 /home/komatsu/allocator/brk/a.out
      0x555555756000     0x555555777000    0x21000        0x0 [heap]
      0x7ffff59e3000     0x7ffff79e4000  0x2001000        0x0
//以下、出力は割愛

「 0x2001000 」を10進数に直すと「33558528」になります。

実は「 33558528 」は 8192×1024×4 の計算結果です。

このサイズは 128KiB を超えるので malloc() ではなく mmap() が代わりにコールされます。

その証拠として新たに作られた領域はアドレスがヒープ領域よりかなり上位のアドレスです。

      0x7ffff59e3000     0x7ffff79e4000  0x2001000        0x0

最後の方は特に代り映えしませんが free() によってメモリーを解放後の状態をチェックしてみると良いかもしれないです。

(gdb) n
third sbrk: 0x7ffff59e3010
18        int *v = (int*)std::malloc(sizeof(int)*8192*1024*1024);
(gdb) n
19        if(v == nullptr)
(gdb) n
20          perror("allocation error v");
(gdb) n
allocation error v: Cannot allocate memory
21        std::printf("final sbrk: 0x%lx\n",(unsigned long)sbrk(0));
(gdb) n
final sbrk: 0x555555777000
22        std::free(p);
(gdb) n
23        std::free(w);
(gdb) n
24      }
(gdb) info proc mapping
process 18340
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555555000     0x1000        0x0 /home/komatsu/allocator/brk/a.out
      0x555555754000     0x555555755000     0x1000        0x0 /home/komatsu/allocator/brk/a.out
      0x555555755000     0x555555756000     0x1000     0x1000 /home/komatsu/allocator/brk/a.out
      0x555555756000     0x555555777000    0x21000        0x0 [heap]

free() 関数でメモリー領域を解放すると「 33558528 」をサイズとした領域が消えてますね。

mmap() の領域は独自の領域なのでヒープとは別にアンマップされます。

Copyright 2018-2019, by Masaki Komatsu