Heap 영역에서는 chunk라는 단위로 메모리를 관리합니다. Chunk에 관한 정의는 malloc.c 파일의 malloc_chunk
구조체로 정의되어 있으며 내용은 아래와 같습니다.
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk, if it is free. */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if this chunk is free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if this chunk is free. */
struct malloc_chunk* bk_nextsize;
};
typedef struct malloc_chunk* mchunkptr;
정의된 내용을 가반으로 chunk의 상태에 따라 allocated chunk와 freed chunk로 구분할 수 있습니다. 두 chunk의 차이점을 설명하기 이전에 먼저 공통점을 설명하겠습니다.
malloc_chunk
구조체에는 두 개의 INTERNAL_SIZE_T
타입의 변수가 존재합니다.
prev_size
의 경우 인접한 이전 chunk의 size 값을 저장하고 있습니다. 이는 인접한 이전 chunk가 freed chunk일 때 값이 저장됩니다.
size
의 경우 현재 chunk의 size 값을 저장하고 있습니다. 여기서 size는 요청한 크기가 아닌 overhead를 포함한 실제 할당된 크기를 의미합니다. size
의 경우 하위 3 bit는 flag 목적으로 예약되어 있습니다.
Chunk의 상태를 관리하기 위한 flag로 순서대로 A, M, P flag가 존재합니다. 각 flag를 가중치 코드로 변환할 경우 A = 4
, M = 2
, P = 1
의 값을 가집니다.
A flag는 해당 chunk가 main arena가 아닌 다른 arena에 의해 관리될 경우 이를 "set" 으로 설정합니다. 따라서 Sub thread에 의해 생성된 chunk는 이 값을 항상 "set" 으로 가지게 됩니다.
mmap()
함수를 통해 할당된 경우 해당 bit를 "set" 으로 설정합니다.
P flag는 물리적으로 인접한 이전 chunk가 사용 중(allocated)일 경우 해당 bit를 "set" 으로 설정합니다. 만약 인접한 이전 chunk가 free 될 경우 현재 chunk의 P flag를 0으로 지정하며 이전 chunk의 size
를 현재 chunk의 prev_size
에 저장합니다.
이론적으로는 chunk의 size에 대한 언급이 존재하지 않지만, 실제 분석을 하게 될 경우 tcache, fastbin에서는 P flag의 변경이 이루어지지 않는 것을 확인할 수 있었습니다. 이에 대한 내용은 free()
함수 분석 시 기술할 예정입니다.
Allocated Chunk의 경우 malloc_chunk
의 구조체에서 fd, bk, fd_nextsize, bk_nextsize 항목을 사용하지 않습니다. 아래 내용은 allocated chunk의 구조를 형상화한 것 입니다.
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, in bytes |A|M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_size() bytes) .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| (size of chunk, but used for application data) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
실제로 할당을 진행하게 되면 return 값이 가리키는 주소는 mem
위치가 됩니다. 또한 기존의 fd, bk, fd_nextsize, bk_nextsize 공간은 User Data를 저장하며 다음 chunk의 prev_size
또한 User Data를 저장하는데 활용될 수 있습니다.
Freed Chunk의 경우 chunk의 size에 따라 다른 동작을 수행하게 됩니다.
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`head:' | Size of chunk, in bytes |A|0|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Forward pointer to next chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Back pointer to previous chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Unused space (may be 0 bytes long) .
. .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`foot:' | Size of chunk, in bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Size에 따라 적절한 처리 과정을 거쳐 bin list에 저장되며 bin list를 유지하기 위해 필요한 값들이 setting 됩니다. 자세한 내용에 대해서는 각 bin list 분석 중 설명할 예정입니다.