第32章 チャンク( malloc_chunk )

 チャンクはデータを保有したり、解放した空き領域、未使用の空き領域を管理するためのオブジェクトです。

 チャンクには 3 種類あります。

 割り当て済みチャンクは、Cのプログラムからアクセスできるチャンクですね。

 フリーチャンクは free() 関数等で割り当て済みチャンクを解放したものです。

 割り当てを行う際には、フリーチャンクを連結したフリーリストというデータ構造から、適当なサイズのチャンクを探してそれを再利用できます。

 トップチャンクは未使用のデータ領域のボーダーのアドレスを見つけるためのタグみたいなものです。

 これら 3 種類のチャンクは malloc_chunk 構造体の状態として表現できます。

 では構造体の定義を見てみましょう。

malloc.c(https://github.com/MacKomatsu/glibc/blob/release/2.27/master/malloc/malloc.c). 

1002 /* Forward declarations.  */
1003 struct malloc_chunk;
1004 typedef struct malloc_chunk* mchunkptr;

malloc.c(https://github.com/MacKomatsu/glibc/blob/release/2.27/master/malloc/malloc.c). 

1054 /*
1055   This struct declaration is misleading (but accurate and necessary).
1056   It declares a "view" into memory allowing access to necessary
1057   fields at known offsets from a given base. See explanation below.
1058 */
1059
1060 struct malloc_chunk {
1061
1062   INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
1063   INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
1064
1065   struct malloc_chunk* fd;         /* double links -- used only if free. */
1066   struct malloc_chunk* bk;
1067
1068   /* Only used for large blocks: pointer to next larger size.  */
1069   struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
1070   struct malloc_chunk* bk_nextsize;
1071 };

 mchunk_previous_size は前のチャンクのサイズですが、フリーリストの連結に使われます。

 mchunk_size は下 3 桁分をフラグに使ったチャンクサイズです。

 フラグについては以下の図を見てください。

図32.1 割り当て済みチャンク(allocated chunk)

img/allocated_chunk.png

 まあ構造は初めて見ると複雑に見えるかと思いますがね。

 それでフラグには以下の 3 種類が存在します。

A(NON_MAIN_ARENA)
メインアリーナでない
M(IS_MMAPED)
mmap() されたチャンクである
P(PREV_INUSE)
前のチャンクが使用中

 Aは 0x4 に相当し M は 0x2、そして P は 0x1 として表現されます。

 ペイロードはアプリケーションの使用するユーザーデータに相当します。

 もしお疑いであれば malloc.c のコメントを確認すると良いでしょう。

/*
1091     chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1092       |             Size of previous chunk, if unallocated (P clear)  |
1093       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1094       |             Size of chunk, in bytes                     |A|M|P|
1095       mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1096       |             User data starts here...                          .
1097       .                                                               .
1098       .             (malloc_usable_size() bytes)                      .
1099       .                                                               |
1100 nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1101       |             (size of chunk, but used for application data)    |
1102       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1103       |             Size of next chunk, in bytes                |A|0|1|
1104       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

 つまりユーザーデータはチャンクサイズのすぐ次に来ることになります。

図32.2 割り当て済みチャンクの内部(allocated chunk)

img/allocated_chunk_data.png

 割り当て済みの領域は malloc_chunk 構造体の疑似バージョンと言えるかもしれませんね。

 ユーザーデータを強引に入れたって感じです。

 反対にフリーチャンクは以下のように malloc_chunk 構造体のパラメーターをフル活用できます。

図32.3 フリーチャンク(free chunk)

img/free_chunk.png

 奇妙に感じられた方は malloc.c の以下のコメントをチェックしてみてくださいね。

/*
1116     chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1117       |             Size of previous chunk, if unallocated (P clear)  |
1118       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1119     `head:' |             Size of chunk, in bytes                     |A|0|P|
1120       mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1121       |             Forward pointer to next chunk in list             |
1122       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1123       |             Back pointer to previous chunk in list            |
1124       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1125       |             Unused space (may be 0 bytes long)                .
1126       .                                                               .
1127       .                                                               |
1128 nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1129     `foot:' |             Size of chunk, in bytes                           |
1130       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1131       |             Size of next chunk, in bytes                |A|0|0|
1132       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

 これを図に直すと以下のようになります。

図32.4 フリーチャンクの内部(small free chunk、大きめでない場合)

img/free_chunk_data.png

 fd_nextsize と bk_nextsize は大きめのフリーチャンク(large free chunk)の時のみに使われるらしいです。

 なので小さめのフリーチャンク(small free chunk)の時には fd_nextsize と bk_nextsize は使われません。

 ちなみにフリーチャンクについては大きさによって、保存されるデータ構造が異なります。

 このチャンクを収納してくれるデータ構造を bin と呼びます。

 bin はポインターの配列ですが、

 ビンの詳細は後で説明しますが、どのビンに入るかによってチャンクの内部データが変動します。

Copyright 2018-2019, by Masaki Komatsu