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