Index
메모리 구조
메모리 구조 중에서 heap을 관리하는 구조와 규칙에 대해서 알아보려고 한다.
heap은 프로그램이 실행되고 동적으로 메모리를 할당하고 해제하는 영역이다. C언어에서는 malloc 함수를 사용하여 메모리를 할당하고, free 함수를 사용하여 할당된 공간을 해제한다.
Heap 영역 할당
malloc 함수 호출 전
malloc 함수 호출 후
heap 공간이 메모리에 할당된 것을 확인할 수 있다. 프로그램이 시작하면서 만들어 진 것이 아닌, 실행 도중 생겨난 영역으로 동적할당이 된 것이다.
chunk 필드
malloc_chunk의 구조는 다음과 같다.
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if 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 free. */
struct malloc_chunk* bk_nextsize;
};
fd, bk, fd_nextsize, bk_nextsize는 free된 경우에만 사용하고, fd_nextsize, bk_nextsize는 크기가 큰 chunk를 관리할 때 사용된다.
먼저 할당된 chunk로 기본적인 구조를 알아보자. glibc-2.27의 malloc.c 파일에서의 할당된 chunk를 나타내는 구조이다.
// line 1116
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|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
하나의 chunk는 크기 정보를 가지는 header와 데이터가 담기는 mem영역으로 구분할 수 있고, nextchunk의 PREV_INUSE(0x1)이 true가 될 것이다.
청크 구조를 그림으로 그려보면 다음과 같다.
prev_size
이전 청크의 크기
size
현재 청크의 크기(32bit : 8바이트 정렬 / 64bit : 16바이트 정렬)
하위 3bit는 필드 상황에 따른 flag bit
- PREV_INUSE (0x1)
- 현재 청크 바로 이전 청크가 할당되어 있는 경우 -> 1로 세팅
- fast bin 크기에 해당하는 경우 free가 되어도 1을 가진다.
- 이전 청크가 있으므로, 병합할 청크가 아니다.
- IS_MMAPED (0x2)
- 현재 청크가 mmap을 통해 할당된 경우 -> 1로 세팅
- 큰 메모리 요청할 경우 mmap함수를 사용하고 bin 내에 속하지 않음.
- munmap함수로 호출 해제
- NON_MAIN_ARENA (0x4)
- 현재 청크가 thread_arena에 위치하는 경우 -> 1로 세팅
data
할당받은 chunk에서 Data가 저장되는 공간이다. 이 공간을 mem이라고 부르기도 한다.
chunk가 해제되었다면, 해제된 chunk 관리를 위해 다른 역할을 하기도 한다.
chunk 종류
chunk는 사용을 위해 할당한 chunk, 해제한 chunk, 그리고 힙 영역에 할당된 나머지 공간을 관리하는 top chunk가 있다. 간단한 예제코드를 통해 Heap 메모리를 할당하고 해제하면서 구조를 확인해보자.
//Name : chunk.c
//Compiler : gcc -o chunk chunk.c -no-pie -fno-stack-protector
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
char *chunk1 = (char *) malloc(8);
char *chunk2 = (char *) malloc(8);
strcpy(chunk1, "HiHi");
strcpy(chunk2, "ByeBye");
free(chunk1)
free(chunk2)
return 0;
}
Allocated chunk
Heap 메모리 사용을 위한 할당된 chunk의 구조
아래 사진은 chunk1과 chunk2를 할당했을 때의 메모리 구조이다.
실제 chunk1의 주소를 가서 확인하면 위와 같이 2개의 allocated chunk를 확인할 수 있다.
size는 0x21(0x20+0x1)이므로, 32바이트 공간을 사용하고 있으며 PREV_INUSE 플래그가 1로 세팅된 상태이다.
strcpy함수로 HiHi와 ByeBye를 각각의 청크에 입력하면 Heap 메모리에 데이터가 저장된다.
Freed chunk
아래 사진은 할당했던 chunk를 사용을 마치고 해제가 된 chunk의 구조이다.
fd와 bk로 관리가 되며, 크기가 큰 chunk의 관리에서는 fd_nextsize와 bk_nextsize 까지 이용해서 freed chunk를 관리한다.
아래는 예제코드에서 할당되었던 두개의 chunk에서 chunk1이 해제된 메모리 구조이다. 예제 코드에서 malloc으로 크기가 크지않게 할당해줬기 때문에 fd와 bk만 사용한다.
chunk1이 free되어 tcachebins에서 해당 chunk를 관리한다. 이 bins는 glibc 2.26버전 이상부터 생겼는데, bins에 대해서는 다음 글에서 자세히 설명할 것이니, 해제된 chunk를 관리하는 공간이라고 이해하고 넘어가자.
chunk2도 해제하면 다음과 같은 구조일 것이다.
free가 되면서 chunk 안의 데이터가 들어갔던 부분이 fd와 bk로 바뀐 것을 확인할 수 있다.
두번째에 해제된 chunk2의 fd 부분은 앞선 해제된 chunk1의 fd의 주소를 가지고 있다. 이것 또한 다음 글에서 자세히 설명할 것이지만, 이러한 해제된 chunk들을 fd와 bk를 이용하여 앞서 언급되었던 bins이라는 곳에서 linked list로 관리되는 것이라고 이해하면 된다.
Top chunk
힙 영역 마지막에 위치한다. 메모리를 할당할 때 각 bins를 확인하고, 마땅한 chunk가 없으면, top chunk에서 분리하여 할당한다. 해제 시에도 top chunk와 붙어있는 chunk가 해제되면 병합 과정을 진행한다.
chunk1만 할당했을 때 Top chunk의 size는 0x20d51이지만, chunk2를 할당할 때 Top chunk에서 분리하여서 할당해주면서 Top chunk의 size는 -0x20되었다. 이런 식으로 분리해주기도 하고, chunk가 해제되면 상황에 맞게 다시 흡수시키기도 한다.
앞에서도 봤던 사진이다. malloc으로 첫 동적할당을 수행할 때 생겨난 heap 영역이다. 이 부분의 Size만큼 Top chunk가 처음에 할당되고, 규칙에 맞게 분리하고 병합하면서 chunk를 관리한다. 오른쪽 사진에서 flag bit를 제외한 Size를 다 더해보면 0x21000이 될 것이다.
정리
이 글을 읽었을 때, 직접 예제 코드를 gdb로 브레이크 포인트를 설정하고 메모리 구조를 확인하자.
malloc 함수가 실행되면서 chunk가 생성되고, 데이터를 쓰고, free함수로 해제된다.
해제되었을 때는 fd와 bk를 이용해서 linked list로 chunk가 관리되며, 관리되는 곳은 bins이다.
top chunk는 unused 상태의 heap 공간을 가지고 있다가, malloc 시 분리하면서 할당하고, 해제될 때도 규칙에 맞게 다시 병합한다.
Uploaded by N2T