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