malloc って裏で何してんでしょうね?
まあ興味もったってプログラマーには影響ないんでしょう… ええ…
(´・ω・`)
確かに強いプログラマーのブログとか読むと glibc とか dlmalloc のアルゴリズムとか完全理解した高位階の魔術を駆使したウィザードリーをチラ見すると、「はえー、すげえ、草」といった感想しかないでしょう…
わかります… (´・ω・`)
仰られる通り通常のプログラマーには不要な技術ではありますが、表層的にではあっても検証してみると以外に楽しかったりしますんで、やってみましょう。
まずは空のプログラムをビルドしてみましょう。
empty.c.
1 int main(){}
ビルドしたバイナリを strace すると以下のようになります。
$ gcc empty.c -g $ strace ./a.out execve("./a.out", ["./a.out"], 0x7fff8c9cf800 /* 57 vars */) = 0 brk(NULL) = 0x55672d205000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/home/komatsu/stderred/build/libstderred.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\23\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=23280, ...}) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f50d732d000 mmap(NULL, 2113992, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f50d6f03000 mprotect(0x7f50d6f07000, 2093056, PROT_NONE) = 0 mmap(0x7f50d7106000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f50d7106000 close(3) = 0 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=127214, ...}) = 0 mmap(NULL, 127214, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f50d730d000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0 mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f50d6b12000 mprotect(0x7f50d6cf9000, 2097152, PROT_NONE) = 0 mmap(0x7f50d6ef9000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f50d6ef9000 mmap(0x7f50d6eff000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f50d6eff000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\16\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0644, st_size=14560, ...}) = 0 mmap(NULL, 2109712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f50d690e000 mprotect(0x7f50d6911000, 2093056, PROT_NONE) = 0 mmap(0x7f50d6b10000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f50d6b10000 close(3) = 0 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f50d730a000 arch_prctl(ARCH_SET_FS, 0x7f50d730a740) = 0 mprotect(0x7f50d6ef9000, 16384, PROT_READ) = 0 mprotect(0x7f50d6b10000, 4096, PROT_READ) = 0 mprotect(0x7f50d7106000, 4096, PROT_READ) = 0 mprotect(0x55672c5b9000, 4096, PROT_READ) = 0 mprotect(0x7f50d732f000, 4096, PROT_READ) = 0 munmap(0x7f50d730d000, 127214) = 0 ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0 exit_group(0) = ? +++ exited with 0 +++
この出力内容では意味不明でしょうが malloc() のコールを追加して見ると良いでしょう。
main.c.
1 #include <stdlib.h> 2 #include <stdio.h> 3 4 int main() 5 { 6 void *ptr = malloc(sizeof(int)); 7 free(ptr); 8 return 0; 9 }
ビルドした実行可能ファイルに strace をした結果を比較すれば malloc() のコールの内部実装がわかろうというものです。
ではやってみましょう。
$ gcc main.c -g $ strace ./a.out execve("./a.out", ["./a.out"], 0x7fffa58495b0 /* 57 vars */) = 0 brk(NULL) = 0x55620ea32000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/home/komatsu/stderred/build/libstderred.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\23\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=23280, ...}) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f23702cc000 mmap(NULL, 2113992, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f236fea2000 mprotect(0x7f236fea6000, 2093056, PROT_NONE) = 0 mmap(0x7f23700a5000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f23700a5000 close(3) = 0 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=127214, ...}) = 0 mmap(NULL, 127214, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f23702ac000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0 mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f236fab1000 mprotect(0x7f236fc98000, 2097152, PROT_NONE) = 0 mmap(0x7f236fe98000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f236fe98000 mmap(0x7f236fe9e000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f236fe9e000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\16\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0644, st_size=14560, ...}) = 0 mmap(NULL, 2109712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f236f8ad000 mprotect(0x7f236f8b0000, 2093056, PROT_NONE) = 0 mmap(0x7f236faaf000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f236faaf000 close(3) = 0 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f23702a9000 arch_prctl(ARCH_SET_FS, 0x7f23702a9740) = 0 mprotect(0x7f236fe98000, 16384, PROT_READ) = 0 mprotect(0x7f236faaf000, 4096, PROT_READ) = 0 mprotect(0x7f23700a5000, 4096, PROT_READ) = 0 mprotect(0x55620e9ce000, 4096, PROT_READ) = 0 mprotect(0x7f23702ce000, 4096, PROT_READ) = 0 munmap(0x7f23702ac000, 127214) = 0 ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0 brk(NULL) = 0x55620ea32000 brk(0x55620ea53000) = 0x55620ea53000 exit_group(0) = ? +++ exited with 0 +++
2 つを比べると最後のほうに2行だけ追加されています。
brk(NULL) = 0x55620ea32000 brk(0x55620ea53000) = 0x55620ea53000
後で詳しめに説明しますが brk() はヒープ領域のアドレス取得や、割り当てを行う関数です。
brk(NULL) は現在のヒープ領域の終端アドレス、そして brk(0x55620ea53000) は 「 0x55620ea32000 」 から 「 0x55620ea53000 」にヒープのアドレスを拡大していますね。
Copyright 2018-2019, by Masaki Komatsu