한양과학기술고등학교

에듀테크 프로젝트 대시보드

메모리 할당자 (malloc)

🧱 메모리 할당자 (Dynamic Memory Allocation)

프로그램이 malloc() 으로 요청한 크기의 공간을 힙(heap) 에서 잘라 주고, free() 로 돌려받는다. 이 과정에서 생기는 단편화(fragmentation) 와, 빈 공간을 관리하는 free list, 그리고 어떤 빈칸을 고를지 정하는 배치 전략을 직접 만져 본다.

1 힙은 하나의 긴 바이트 배열이다

운영체제는 프로세스에게 큰 연속 메모리 영역(힙)을 준다. 할당자는 이 영역을 블록(block) 단위로 쪼개 관리한다. 각 블록 앞에는 헤더가 붙어 "이 블록의 크기 / 사용 중인지" 를 기록한다. free(p) 는 포인터 바로 앞의 헤더를 보고 크기를 알아낸다 — 그래서 크기를 안 넘겨줘도 된다.

요청 크기는 보통 8/16바이트 정렬(alignment) 단위로 올림된다. 13바이트를 요청해도 16바이트를 주는 식인데, 이때 쓰이지 않고 버려지는 3바이트가 내부 단편화 다.

2 직접 할당해 보기

아래는 64칸짜리 힙이다. 크기를 정해 malloc 을 누르면 전략에 맞는 빈칸에 색 블록이 생기고, 색 블록을 클릭하면 free 된다. 인접한 빈칸은 자동으로 병합(coalescing) 된다. 빈칸 총합은 넉넉한데도 큰 블록이 안 들어가는 순간이 외부 단편화 다.

016324864
크기 6
전략
0
사용 중 (칸)
64
빈 공간 합계
64
가장 큰 빈칸
0%
외부 단편화

3 Free list — 빈 블록의 연결 리스트

사용 중인 블록은 프로그램이 포인터로 들고 있으니 할당자가 신경 쓸 필요가 없다. 할당자가 추적해야 하는 건 빈 블록들뿐이다. 빈 블록의 본문 공간에 "다음 빈 블록 주소"를 적어 두면, 추가 메모리 없이 연결 리스트가 만들어진다. malloc 은 이 리스트를 훑어 맞는 칸을 찾고, free 는 이 리스트에 블록을 다시 매단다.

4 배치 전략 비교

전략고르는 칸장점약점
First-fit앞에서부터 처음 맞는 칸탐색이 빠름앞쪽에 작은 조각이 쌓임
Best-fit남는 공간이 최소인 칸큰 칸을 아껴 둠전부 훑어야 함, 아주 작은 조각 양산
Worst-fit가장 큰 칸남는 조각이 그나마 큼큰 칸이 금방 사라짐, 성능 나쁨

실제 할당자(glibc ptmalloc, jemalloc, tcmalloc)는 크기별로 빈 블록을 버킷(bins/size class) 으로 나눠 관리해 탐색을 O(1) 에 가깝게 만들고, 스레드마다 arena 를 둬 락 경합을 줄인다.

5 뼈대 코드 — 헤더 + free list

// 블록 헤더: 포인터 바로 앞 8바이트에 숨어 있다
typedef struct Block {
    size_t size;          // 본문 크기
    int    free;          // 1이면 빈 블록
    struct Block *next;   // free list 연결
} Block;

void *my_malloc(size_t want) {
    want = align8(want);                 // 8바이트 정렬 → 내부 단편화
    for (Block *b = free_head; b; b = b->next) {
        if (b->free && b->size >= want) {   // first-fit
            split_if_large(b, want);        // 남으면 잘라서 새 빈 블록
            b->free = 0;
            return (char*)b + sizeof(Block);
        }
    }
    return extend_heap(want);            // 부족하면 sbrk/mmap 으로 확장
}

void my_free(void *p) {
    Block *b = (Block*)((char*)p - sizeof(Block));
    b->free = 1;
    coalesce(b);                       // 인접한 빈 블록과 병합
}

댕글링 포인터(free 후 사용), 이중 free, 버퍼 오버플로로 헤더 훼손 같은 버그가 모두 이 구조 위에서 일어난다. 그래서 use-after-free 는 대표적인 보안 취약점이 된다.