https://github.com/IMGyuGo/malloc_lab_docker
GitHub - IMGyuGo/malloc_lab_docker: malloc_lab_docker
malloc_lab_docker. Contribute to IMGyuGo/malloc_lab_docker development by creating an account on GitHub.
github.com
branch 기준
implicit (묵시적 가용 리스트)
explicit (명시적 가용 리스트 - LIFO 저장 방식)
segregated (분리 가용 리스트 - buddy system)
bestfit (분리 가용 리스트에 bestfit 적용)
binpolicy (범위 분할, 128byte보다 작은 크기 찾을 때는 first fit, 큰 크기 찾을 때는 best fit)
- memlib.c
더보기
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include "memlib.h"
#include "config.h"
/* 비공개 변수 */
static char *mem_start_brk; /* 힙의 첫 바이트를 가리킴 */
static char *mem_brk; /* 힙의 마지막 바이트를 가리킴 */
static char *mem_max_addr; /* 허용되는 최대 힙 주소 */
/*
* mem_init - 메모리 시스템 모델 초기화
*/
void mem_init(void)
{
/* 가용 VM을 모델링할 저장 공간 할당 */
if ((mem_start_brk = (char *)malloc(MAX_HEAP)) == NULL) {
fprintf(stderr, "mem_init_vm: malloc error\n");
exit(1);
}
mem_max_addr = mem_start_brk + MAX_HEAP; /* 허용 최대 힙 주소 */
mem_brk = mem_start_brk; /* 처음에는 힙이 비어 있음 */
}
/*
* mem_deinit - 메모리 시스템 모델이 쓰던 저장 공간 해제
*/
void mem_deinit(void)
{
free(mem_start_brk);
}
/*
* mem_reset_brk - 시뮬레이션 brk를 비어 있는 힙으로 리셋
*/
void mem_reset_brk()
{
mem_brk = mem_start_brk;
}
/*
* mem_sbrk - sbrk 함수의 단순 모델. 힙을 incr 바이트만큼 확장하고
* 새 영역의 시작 주소를 반환한다. 이 모델에서는 힙을 줄일 수 없다.
*/
void *mem_sbrk(int incr)
{
char *old_brk = mem_brk;
if ( (incr < 0) || ((mem_brk + incr) > mem_max_addr)) {
errno = ENOMEM;
fprintf(stderr, "ERROR: mem_sbrk failed. Ran out of memory...\n");
return (void *)-1;
}
mem_brk += incr;
return (void *)old_brk;
}
/*
* mem_heap_lo - 힙 첫 바이트 주소 반환
*/
void *mem_heap_lo()
{
return (void *)mem_start_brk;
}
/*
* mem_heap_hi - 힙 마지막 바이트 주소 반환
*/
void *mem_heap_hi()
{
return (void *)(mem_brk - 1);
}
/*
* mem_heapsize() - 힙 크기(바이트) 반환
*/
size_t mem_heapsize()
{
return (size_t)(mem_brk - mem_start_brk);
}
/*
* mem_pagesize() - 시스템 페이지 크기 반환
*/
size_t mem_pagesize()
{
return (size_t)getpagesize();
}
1. implicit free list
더보기
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include "mm.h"
#include "memlib.h"
/* 단일 워드(4) 또는 더블 워드(8) 정렬 */
#define WSIZE 4
#define DSIZE 8
#define CHUNKSIZE (1 << 12)
#define ALIGNMENT 8
/* 크기를 ALIGNMENT의 배수로 올림 */
/*
예를 들어, 15byte를 할당 시,
(15 + 7)
10110 & 11111000 = 10000 -> (16)
예를 들어, 16byte를 할당 시,
(16 + 7)
10111 & 11111000 = 10000 -> (16)
예를 들어, 17byte를 할당 시,
(17 + 7)
11000 & 11111000 = 11000 -> (24)
즉, 올림처리
*/
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
/**
* 사이즈를 지정할 때, 왜 size_t로 8바이트 데이터 타입을 쓰는지 생각해보니
* 메모리 용량이 4바이트일 경우 2^32 = 2^10 = 1000byte -> (1000)^3 * 2^2 = 4 * 10^9 = 4,000,000kB = 4,000Mb = 4Gbyte
* 메모리 용량이 4기가 보다 더 클 수도 있기 때문에 (나의 생각)
*/
#define PACK(size, alloc) ((size) | (alloc))
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
// 여기서 footer를 안 쓰면, footer에 정보를 넣을 수 가 없다.
#define HDRP(bp) ((char *)(bp) - WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
static char *heap_listp;
static void *extend_heap(size_t words);
static void *coalesce(void *bp);
static char *find_fit(size_t size);
static void place(char *p, size_t asize);
/*
* mm_init - malloc 패키지 초기화.
*/
int mm_init(void)
{
// (void*) -1
/**
* (void *)는 모든 타입을 저장할 수 있는 저장소 (주소만 알 수 있음)
* 즉, 어떤 타입이든 담을 수 있는 generic pointer
* 주소만 가지고 있고, 역참조하려면 캐스팅 필요
*
* (void*) -1 (유효하지 않은 주소)
* 보통 시스템 콜 실패 시 반환값!
*/
if ((heap_listp = mem_sbrk(2 * DSIZE)) == (void *)-1)
return -1;
PUT(heap_listp, 0); // 4 byte 패딩 설정
PUT(heap_listp + (1 * WSIZE), PACK(DSIZE, 1)); // prologue header
PUT(heap_listp + (2 * WSIZE), PACK(DSIZE, 1)); // prologue footer
PUT(heap_listp + (3 * WSIZE), PACK(0, 1)); // Epilogue header
// PUT(heap_listp, PACK(DSIZE, 1)); // prologue header
// PUT(heap_listp + (1 * DSIZE), PACK(0, 1)); // Epilogue header
heap_listp += DSIZE; // Payload 위치 설정
if (extend_heap(CHUNKSIZE / WSIZE) == NULL)
return -1;
return 0;
}
static void *extend_heap(size_t words)
{
char *bp;
size_t size;
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
if ((long)(bp = mem_sbrk(size)) == -1)
{
return NULL;
}
// 이거 때문에 초기화 할 때, 패딩과 프롤로그 헤더를 따로 만든다.
PUT(HDRP(bp), PACK(size, 0)); // free block header
PUT(FTRP(bp), PACK(size, 0)); // free block footer
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); // new epilogue header
// 힙을 늘릴 때, 앞에 있는 것이 Free일 수 도 있기 때문
// 힙 확장 전략을 고려한 것 (My Think 검증 아직 안함)
return coalesce(bp);
}
static void *coalesce(void *bp)
{
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp)));
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && next_alloc)
{
return bp;
}
else if (prev_alloc && !next_alloc)
{
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
else if (!prev_alloc && next_alloc)
{
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
else
{
size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
return bp;
}
/*
* mm_malloc - brk 포인터를 늘려 블록 할당.
* 항상 정렬 배수 크기의 블록을 할당한다.
*/
void *mm_malloc(size_t size)
{
size_t asize;
size_t extendsize;
char *bp;
if (size == 0)
return NULL;
if (size <= DSIZE)
asize = 2 * DSIZE;
else
asize = ALIGN(size + SIZE_T_SIZE);
// 할당 하기 전에 영역 전부를 다 살펴보고
// place를 통해 그 자리에 할당
if ((bp = find_fit(asize)) != NULL)
{
place(bp, asize);
return bp;
}
// 이걸 왜하지? 무조건 CHUNKSIZE만큼만 확장하면 안되나?
// 정말 모르겠지만,g.g.
extendsize = MAX(asize, CHUNKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}
/**
* find_fit
*/
static char *find_fit(size_t size)
{
// 힙 주소를 first_fit으로 찾는 방식 고안
// 만약 next_fit이라면, 이미 전역 포인터 변수가 하나 더 있어야 할 거 같다.(내 생각)
char *bp = heap_listp + DSIZE;
while (1)
{
// char *pred_header = HDRP(PREV_BLKP(bp)); // 전임자의 HEADER
// char *pred_footer = FTRP(PREV_BLKP(bp)); // 전임자의 FOOTER
// char *succ_header = HDRP(NEXT_BLKP(bp)); // 후임자의 HEADER
// char *succ_footer = FTRP(NEXT_BLKP(bp)); // 후임자의 FOOTER
// size_t is_pred_alloc = GET_ALLOC(pred_footer); // 이전이 alloc인지?
size_t is_alloc = GET_ALLOC(HDRP(bp)); // 현재가 alloc인지? -> FTRP(bp) 써도 됨
// size_t is_succ_alloc = GET_ALLOC(succ_header); // 이후가 alloc인지?
/**
* 1. size <= free_size 일 경우
* 2. alloc이 아닌 경우
**/
size_t free_size = GET_SIZE(HDRP(bp));
if (free_size == 0)
return NULL;
if (is_alloc || free_size < size)
{
bp += free_size;
continue;
}
return bp;
}
}
static void place(char *p, size_t asize)
{
// 실제 사이즈
size_t real_size = GET_SIZE(HDRP(p));
if ((real_size - asize) < 2 * DSIZE)
{
PUT(HDRP(p), PACK(real_size, 1));
PUT(FTRP(p), PACK(real_size, 1));
}
else
{
PUT(HDRP(p), PACK(asize, 1));
PUT(FTRP(p), PACK(asize, 1));
char *split_ptr = NEXT_BLKP(p);
PUT(HDRP(split_ptr), PACK(real_size - asize, 0));
PUT(FTRP(split_ptr), PACK(real_size - asize, 0));
coalesce(split_ptr);
}
}
/*
* mm_free - 블록 해제 시 아무 동작도 하지 않음.
*/
void mm_free(void *ptr)
{
// *(size_t *)ptr = 0;
size_t size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
coalesce(ptr);
}
void *mm_realloc(void *ptr, size_t size)
{
void *newptr;
void *bp = ptr;
size_t all_size = ALIGN(size) + DSIZE;
size_t cur_size = GET_SIZE(HDRP(bp));
size_t next_size = GET_SIZE(HDRP(NEXT_BLKP(bp)));
// if (size == 0)
// {
// mm_free(bp);
// return NULL;
// }
if (all_size <= cur_size)
{
// PUT(HDRP(bp), PACK(all_size, 0));
// PUT(FTRP(bp), PACK(all_size, 0));
return bp;
}
if (ptr == NULL)
{
return mm_malloc(size);
}
if ((!GET_ALLOC(HDRP(NEXT_BLKP(bp)))) &&
(all_size <= cur_size + next_size))
{
PUT(HDRP(bp), PACK((cur_size + next_size), 0));
PUT(FTRP(bp), PACK((cur_size + next_size), 0));
place(bp, all_size);
return bp;
}
else
{
newptr = find_fit(all_size);
if (newptr == NULL)
{
newptr = extend_heap(MAX(all_size - cur_size, CHUNKSIZE) / WSIZE);
next_size = GET_SIZE(HDRP(NEXT_BLKP(bp)));
if (!GET_ALLOC(HDRP(NEXT_BLKP(bp))) && all_size <= cur_size + next_size)
{
PUT(HDRP(bp), PACK((cur_size + next_size), 0));
PUT(FTRP(bp), PACK((cur_size + next_size), 0));
place(bp, all_size);
return bp;
}
// alloc free alloc epilogue 블럭일 경우, free 부분을 확장 후, 뒤로 옮기기 위해서 따로 조건을 추가
place(newptr, all_size);
memcpy(newptr, bp, cur_size - DSIZE);
mm_free(bp);
return newptr;
}
place(newptr, all_size);
memcpy(newptr, bp, cur_size - DSIZE);
mm_free(bp);
return newptr;
}
}
2. Explicit Free List
더보기
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include "mm.h"
#include "memlib.h"
/* 단일 워드(4) 또는 더블 워드(8) 정렬 */
#define WSIZE 4
#define DSIZE 8
#define CHUNKSIZE (1 << 10) //(1 << 12)
#define ALIGNMENT 8
/* 크기를 ALIGNMENT의 배수로 올림 */
/*
예를 들어, 15byte를 할당 시,
(15 + 7)
10110 & 11111000 = 10000 -> (16)
예를 들어, 16byte를 할당 시,
(16 + 7)
10111 & 11111000 = 10000 -> (16)
예를 들어, 17byte를 할당 시,
(17 + 7)
11000 & 11111000 = 11000 -> (24)
즉, 올림처리
*/
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MINBLOCKSIZE (DSIZE + (2 * SIZE_T_SIZE))
/**
* 사이즈를 지정할 때, 왜 size_t로 8바이트 데이터 타입을 쓰는지 생각해보니
* 메모리 용량이 4바이트일 경우 2^32 = 2^10 = 1000byte -> (1000)^3 * 2^2 = 4 * 10^9 = 4,000,000kB = 4,000Mb = 4Gbyte
* 메모리 용량이 4기가 보다 더 클 수도 있기 때문에 (나의 생각)
*/
#define PACK(size, alloc) ((size) | (alloc))
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
#define GET2(p) (*(void **)(p)) // 포인터 값을 조회하기 위한 용도
#define PUT2(p, val) (*(void **)(p) = (val)) // 포인터 값을 저장하기 위한 용도
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
// 여기서 footer를 안 쓰면, footer에 정보를 넣을 수 가 없다.
#define HDRP(bp) ((char *)(bp) - WSIZE)
/**
* 내 생각
* 4바이트씩 PRED, SUCC 할당하면 8바이트만 늘리면 되고, 4바이트 안에 주소가 다 넣을 정도의 예시들만 있을 것 같다.
* 원래 64bit 컴퓨터라면 8바이트를 할당해야 하긴 한다.
* -> 물어보니
* 무조건 포인터 크기는 플랫폼에 따라 고정되므로
* 할당하는 데이터 크기 (4바이트냐 8바이트냐) 상관없이 주소를 저장하려면 포인터 크기만큼 무조건 필요
*/
#define PRED(bp) ((char *)(bp)) // 현재 bp의 PRED가 저장되어 있는 위치
#define SUCC(bp) ((char *)(bp) + SIZE_T_SIZE) // 현재 bp의 SUCC가 저장되어 있는 위치
#define GET_PRED(bp) ((char *)GET2(PRED(bp)))
#define GET_SUCC(bp) ((char *)GET2(SUCC(bp)))
#define SET_PRED(bp, ptr) (PUT2(PRED(bp), (ptr)))
#define SET_SUCC(bp, ptr) (PUT2(SUCC(bp), (ptr)))
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
static char *heap_listp;
// 가용 리스트에 제일 앞에 담기는 것
static char *heap_free_listp;
static void *extend_heap(size_t words);
static void *coalesce(void *bp);
static char *find_fit(size_t size);
static void place(char *bp, size_t asize);
static void place_allocated(char *bp, size_t block_size, size_t asize, int coalesce_split);
static void push_free(void *p);
static void rem_free(void *p);
/*
* mm_init - malloc 패키지 초기화.
*/
int mm_init(void)
{
// (void*) -1
/**
* (void *)는 모든 타입을 저장할 수 있는 저장소 (주소만 알 수 있음)
* 즉, 어떤 타입이든 담을 수 있는 generic pointer
* 주소만 가지고 있고, 역참조하려면 캐스팅 필요
*
* (void*) -1 (유효하지 않은 주소)
* 보통 시스템 콜 실패 시 반환값!
*/
if ((heap_listp = mem_sbrk(4 * WSIZE)) == (void *)-1)
return -1;
PUT(heap_listp, 0); // 4 byte 패딩 설정
PUT(heap_listp + (1 * WSIZE), PACK(DSIZE, 1)); // prologue header
PUT(heap_listp + (2 * WSIZE), PACK(DSIZE, 1)); // prologue footer
PUT(heap_listp + (3 * WSIZE), PACK(0, 1)); // epilogue header
heap_listp += (2 * WSIZE); // Payload 위치 설정 (prologue payload)
heap_free_listp = NULL;
if (extend_heap(CHUNKSIZE / WSIZE) == NULL)
return -1;
return 0;
}
static void *extend_heap(size_t words)
{
char *bp;
size_t size;
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
if ((bp = mem_sbrk(size)) == (void *)-1)
{
return NULL;
}
// 이거 때문에 초기화 할 때, 패딩과 프롤로그 헤더를 따로 만든다.
PUT(HDRP(bp), PACK(size, 0)); // free block header
PUT(FTRP(bp), PACK(size, 0)); // free block footer
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); // new epilogue header
// 힙을 늘릴 때, 앞에 있는 것이 Free일 수 도 있기 때문
// 힙 확장 전략을 고려한 것 (My Think 검증 아직 안함)
return coalesce(bp);
}
// heap_free_listp에 스택 방식으로 집어넣기
static void push_free(void *p)
{
/*
* HINT (Explicit free list, LIFO/stack insert):
* - free list의 "head"를 `heap_free_listp`로 둔다.
* - 새로 free된 블록 `p`를 항상 head에 꽂으면 LIFO(스택) 방식이 된다.
*
* 연결 규칙(개념):
* - p->pred = NULL
* - p->succ = old_head
* - if (old_head != NULL) old_head->pred = p
* - heap_free_listp = p
*
* 구현 시에는 위의 `pred/succ`를 여기서 만든 매크로를 써서 접근:
* - `PUT(PRED(p), ...)`, `PUT(SUCC(p), ...)` 또는 `GET_PRED/GET_SUCC` 조합
* - 주의: `PRED(bp)`/`SUCC(bp)`는 "저장 위치"이고, `GET_PRED/GET_SUCC`는 "저장된 포인터 값"이다.
*/
char *old_ptr;
if (p == NULL)
{
return;
}
old_ptr = heap_free_listp;
/**
* PUT2(PRED(p), NULL);
* PUT2(SUCC(p), old_ptr);
*/
SET_PRED(p, NULL);
SET_SUCC(p, old_ptr);
if (old_ptr != NULL)
{
SET_PRED(old_ptr, p);
}
heap_free_listp = p;
}
// heap_free_listp에 스택 방식으로 빼기
static void rem_free(void *p)
{
/*
* HINT (remove a free block from explicit list):
* - 제거 대상 `p`의 pred/succ를 읽어서 양 옆을 서로 연결해준다.
*
* 케이스 분기(개념):
* - if (pred == NULL) => p가 head
* heap_free_listp = succ
* if (succ != NULL) succ->pred = NULL
* - else => p가 중간/끝
* pred->succ = succ
* if (succ != NULL) succ->pred = pred
*
* 선택 힌트:
* - 디버깅 쉽게 하려면 제거 후 `p->pred`, `p->succ`를 NULL로 지워도 된다(필수 아님).
* - 인자로 void*만 받으면 제거할 블록을 못 고르니, 보통 시그니처는 `rem_free(char *p)`처럼 "대상 포인터"가 필요하다.
*/
char *pred;
char *succ;
// 중요!!
if (p == NULL)
{
return;
}
/**
* GET2(PRED(p));
* GET2(SUCC(p));
*/
pred = GET_PRED(p);
succ = GET_SUCC(p);
/**
* 만약 없어진게 첫번째꺼라면
* free list 첫번째에 값 집어넣기
**/
if (pred == NULL)
{
heap_free_listp = succ;
}
else
{
// 앞에 있는거의 뒤를 다음걸로 설정
SET_SUCC(pred, succ);
}
// 만약 마지막이 아니라면,
if (succ != NULL)
{
// 뒤에 있는거의 앞을 이전걸로 설정
SET_PRED(succ, pred);
}
// 중요!!
SET_PRED(p, NULL);
SET_SUCC(p, NULL);
}
static void *coalesce(void *bp)
{
char *prev_bp = PREV_BLKP(bp);
char *next_bp = NEXT_BLKP(bp);
size_t prev_alloc = GET_ALLOC(FTRP(prev_bp));
size_t next_alloc = GET_ALLOC(HDRP(next_bp));
size_t size = GET_SIZE(HDRP(bp));
// free 전략을 취할 때,
// 합치면서 heap_free_listp에 있는거 제거 후 다시 추가
// 여기서 나는 계속 rem_free(bp)를 해줬었는데,
// 아직 bp는 생각해보니 heap_free_listp에 들어가지 않은 상태였다.. 할필요가 없었다.. (crash 발생 원인..ㅜㅜ)
if (!prev_alloc)
{
rem_free(prev_bp);
size += GET_SIZE(HDRP(prev_bp));
bp = prev_bp;
}
if (!next_alloc)
{
rem_free(next_bp);
size += GET_SIZE(HDRP(next_bp));
}
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
push_free(bp);
return bp;
}
static void place_allocated(char *bp, size_t block_size, size_t asize, int coalesce_split)
{
size_t remain_size = block_size - asize;
char *split_ptr;
if (remain_size < MINBLOCKSIZE)
{
PUT(HDRP(bp), PACK(block_size, 1));
PUT(FTRP(bp), PACK(block_size, 1));
return;
}
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
split_ptr = NEXT_BLKP(bp);
PUT(HDRP(split_ptr), PACK(remain_size, 0));
PUT(FTRP(split_ptr), PACK(remain_size, 0));
// realloc처럼 allocated block 중간에서 split한 free block은 다음 블록과 바로 이어질 수 있으므로 coalesce 경로가 필요하다.
// mm_malloc에서 원래 free block을 잘라 쓰는 경우에는 이미 coalescing이 끝난 상태라 push_free만 해도 된다.
if (coalesce_split)
{
coalesce(split_ptr);
}
else
{
push_free(split_ptr);
}
}
/*
* mm_malloc - brk 포인터를 늘려 블록 할당.
* 항상 정렬 배수 크기의 블록을 할당한다.
*/
void *mm_malloc(size_t size)
{
size_t asize;
size_t extendsize;
char *bp;
if (size == 0)
return NULL;
/**
* explicit free list의 경우
* pred, succ가 8바이트씩 더 잡는다고 생각해서 무조건 32byte가 최소단위라고 생각했는데,
* 아니고 기존 payload 위치를 pred, succ가 대체한다고 생각해야함..
*
* size의 경우는 예제에 따라 4byte로 넣어도 상관없지만
* 주소는 64bit의 컴퓨터의 경우 환경에 따라 8byte로 주소가 들어갈 수 있으므로
*
* MINBLOCKSIZE = 24 byte
*/
asize = MAX(ALIGN(size + DSIZE), MINBLOCKSIZE);
// 할당 하기 전에 영역 전부를 다 살펴보고
// place를 통해 그 자리에 할당
if ((bp = find_fit(asize)) != NULL)
{
place(bp, asize);
return bp;
}
extendsize = MAX(asize, CHUNKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}
/**
* find_fit
*/
static char *find_fit(size_t size)
{
// 힙 주소를 first_fit으로 찾는 방식 고안
// 만약 next_fit이라면, 이미 전역 포인터 변수가 하나 더 있어야 할 거 같다.(내 생각)
char *bp = heap_free_listp;
while (bp != NULL)
{
// char *pred_header = HDRP(PREV_BLKP(bp)); // 전임자의 HEADER
// char *pred_footer = FTRP(PREV_BLKP(bp)); // 전임자의 FOOTER
// char *succ_header = HDRP(NEXT_BLKP(bp)); // 후임자의 HEADER
// char *succ_footer = FTRP(NEXT_BLKP(bp)); // 후임자의 FOOTER
// size_t is_pred_alloc = GET_ALLOC(pred_footer); // 이전이 alloc인지?
// size_t is_succ_alloc = GET_ALLOC(succ_header); // 이후가 alloc인지?
/**
* 1. size <= free_size 일 경우
* 2. alloc이 아닌 경우
**/
size_t free_size = GET_SIZE(HDRP(bp));
if (!GET_ALLOC(HDRP(bp)) && free_size >= size)
{
return bp;
}
bp = GET_SUCC(bp);
}
return NULL;
}
static void place(char *p, size_t asize)
{
// 실제 사이즈
size_t real_size = GET_SIZE(HDRP(p));
rem_free(p);
place_allocated(p, real_size, asize, 0);
}
/*
* mm_free - 블록 해제 시 아무 동작도 하지 않음.
*/
void mm_free(void *ptr)
{
if (ptr == NULL)
{
return;
}
// *(size_t *)ptr = 0;
size_t size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
coalesce(ptr);
}
// /*
// * mm_realloc - mm_malloc과 mm_free만으로 단순 구현
// */
void *mm_realloc(void *ptr, size_t size)
{
// free alloc free
// 앞을 봐도 될 것 같은데? -> 그치만 메모리 효율성이 많이 떨어질 것 같다.
// 1. 사이즈가 감소할 경우 -> place로 자르기
// 2. 뒤에 free가 있고, 뒤에 사이즈를 더한 값이 size보다 클 경우에는 딱 충분한 크기만큼만 확장 (free일 경우) - place를 사용해서 구현
// 3. 뒤가 확장이 불가능한 eplilogue header일 경우
// 4. 이 경우들 다 빼고 예외상황에는 단순구현으로
void *newptr;
size_t asize;
size_t old_block_size;
size_t next_block_size;
size_t copySize;
char *next_bp;
if (ptr == NULL)
{
return mm_malloc(size);
}
if (size == 0)
{
mm_free(ptr);
return NULL;
}
asize = MAX(ALIGN(size + DSIZE), MINBLOCKSIZE);
old_block_size = GET_SIZE(HDRP(ptr));
// 1. 요청 크기가 더 작아진 경우에는 같은 자리에서 바로 잘라서 남는 부분만 free block으로 돌려준다.
if (asize <= old_block_size)
{
place_allocated(ptr, old_block_size, asize, 1);
return ptr;
}
next_bp = NEXT_BLKP(ptr);
// 2. 뒤 블록이 free면 현재 블록과 합쳐서 in-place 확장을 먼저 시도한다.
if (!GET_ALLOC(HDRP(next_bp)))
{
next_block_size = GET_SIZE(HDRP(next_bp));
if ((old_block_size + next_block_size) >= asize)
{
rem_free(next_bp);
place_allocated(ptr, old_block_size + next_block_size, asize, 1);
return ptr;
}
}
// 3. 바로 뒤가 epilogue면 heap을 더 늘린 다음 다시 한 번 "뒤 free 확장" 경로를 태운다.
if (GET_SIZE(HDRP(next_bp)) == 0)
{
size_t extend_size = MAX(asize - old_block_size, CHUNKSIZE);
// 두번째 : 확장할 수 있는 만큼만 확장
size_t added_size = asize - old_block_size;
if (extend_heap(added_size / WSIZE) != NULL)
{
next_bp = NEXT_BLKP(ptr);
next_block_size = GET_SIZE(HDRP(next_bp));
if (!GET_ALLOC(HDRP(next_bp)) && (old_block_size + next_block_size) >= asize)
{
rem_free(next_bp);
place_allocated(ptr, old_block_size + next_block_size, asize, 1);
return ptr;
}
}
}
// 4. 위 경로들로 해결되지 않는 경우에만 새 블록을 잡고 복사하는 단순 구현으로 fallback 한다.
newptr = mm_malloc(size);
if (newptr == NULL)
return NULL;
copySize = old_block_size - DSIZE;
if (size < copySize)
copySize = size;
memcpy(newptr, ptr, copySize);
mm_free(ptr);
return newptr;
}
3. Segregated Free List
더보기
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include "mm.h"
#include "memlib.h"
/* 단일 워드(4) 또는 더블 워드(8) 정렬 */
#define WSIZE 4
#define DSIZE 8
#define CHUNKSIZE (1 << 10) //(1 << 12)
#define ALIGNMENT 8
/* 크기를 ALIGNMENT의 배수로 올림 */
/*
예를 들어, 15byte를 할당 시,
(15 + 7)
10110 & 11111000 = 10000 -> (16)
예를 들어, 16byte를 할당 시,
(16 + 7)
10111 & 11111000 = 10000 -> (16)
예를 들어, 17byte를 할당 시,
(17 + 7)
11000 & 11111000 = 11000 -> (24)
즉, 올림처리
*/
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MINBLOCKSIZE (DSIZE + (2 * SIZE_T_SIZE))
#define SEG_LIST_COUNT 16
#define SEG_ROOT_BYTES (SEG_LIST_COUNT * SIZE_T_SIZE)
#define SEG_MAX_INDEX (SEG_LIST_COUNT - 1)
#define SEG_INDEX(size) \
((size) <= MINBLOCKSIZE ? 0 \
: (size) <= 1 << 5 ? 1 \
: (size) <= 1 << 6 ? 2 \
: (size) <= 1 << 7 ? 3 \
: (size) <= 1 << 8 ? 4 \
: (size) <= 1 << 9 ? 5 \
: (size) <= 1 << 10 ? 6 \
: (size) <= 1 << 11 ? 7 \
: (size) <= 1 << 12 ? 8 \
: (size) <= 1 << 13 ? 9 \
: (size) <= 1 << 14 ? 10 \
: (size) <= 1 << 15 ? 11 \
: (size) <= 1 << 16 ? 12 \
: (size) <= 1 << 17 ? 13 \
: (size) <= 1 << 18 ? 14 \
: 15)
/**
* 사이즈를 지정할 때, 왜 size_t로 8바이트 데이터 타입을 쓰는지 생각해보니
* 메모리 용량이 4바이트일 경우 2^32 = 2^10 = 1000byte -> (1000)^3 * 2^2 = 4 * 10^9 = 4,000,000kB = 4,000Mb = 4Gbyte
* 메모리 용량이 4기가 보다 더 클 수도 있기 때문에 (나의 생각)
*/
#define PACK(size, alloc) ((size) | (alloc))
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
#define GET2(p) (*(void **)(p)) // 포인터 값을 조회하기 위한 용도
#define PUT2(p, val) (*(void **)(p) = (val)) // 포인터 값을 저장하기 위한 용도
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
// 여기서 footer를 안 쓰면, footer에 정보를 넣을 수 가 없다.
#define HDRP(bp) ((char *)(bp) - WSIZE)
/**
* 내 생각
* 4바이트씩 PRED, SUCC 할당하면 8바이트만 늘리면 되고, 4바이트 안에 주소가 다 넣을 정도의 예시들만 있을 것 같다.
* 원래 64bit 컴퓨터라면 8바이트를 할당해야 하긴 한다.
* -> 물어보니
* 무조건 포인터 크기는 플랫폼에 따라 고정되므로
* 할당하는 데이터 크기 (4바이트냐 8바이트냐) 상관없이 주소를 저장하려면 포인터 크기만큼 무조건 필요
*/
#define PRED(bp) ((char *)(bp)) // 현재 bp의 PRED가 저장되어 있는 위치
#define SUCC(bp) ((char *)(bp) + SIZE_T_SIZE) // 현재 bp의 SUCC가 저장되어 있는 위치
#define GET_PRED(bp) ((char *)GET2(PRED(bp)))
#define GET_SUCC(bp) ((char *)GET2(SUCC(bp)))
#define SET_PRED(bp, ptr) (PUT2(PRED(bp), (ptr)))
#define SET_SUCC(bp, ptr) (PUT2(SUCC(bp), (ptr)))
#define SEG_HEAD(index) ((char *)(seg_free_listp + ((index) * SIZE_T_SIZE)))
#define GET_SEG_HEAD(index) ((char *)GET2(SEG_HEAD(index)))
#define SET_SEG_HEAD(index, ptr) (PUT2(SEG_HEAD(index), (ptr)))
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
static char *heap_listp;
// 가용 리스트에 제일 앞에 담기는 것
// 정해진 리스트의 개수를 배열로 담는 그릇
static char *seg_free_listp;
/*
* SEG LIST 전환 힌트
* - 지금은 `heap_free_listp` 하나만 쓰는 단일 explicit free list 구조다.
* - 다음 단계에서는 `static char *seg_free_listp[SEG_LIST_COUNT];` 같은 head 배열로 바꾸는 쪽이 가장 단순하다.
* - `SEG_INDEX(size)`는 "이 크기의 free block이 어느 리스트로 들어가야 하는가"를 빠르게 정하는 용도다.
*
* 함수 추가/수정 힌트
* - `mm_init`
* 모든 seglist head를 NULL로 초기화해야 한다.
* 만약 head 배열을 힙 앞부분에 둘 생각이면 `SEG_ROOT_BYTES`만큼 prologue 전에 추가 공간을 확보하면 된다.
*
* - `push_free`
* 이제는 단일 head가 아니라 `SEG_INDEX(GET_SIZE(HDRP(bp)))`로 class를 구해서
* 해당 class head에만 LIFO 방식으로 insert하면 된다.
*
* - `rem_free`
* 제거할 블록의 현재 크기로 class를 다시 구한 뒤,
* 그 class list 안에서만 pred/succ를 끊어주면 된다.
*
* - `find_fit`
* 요청 크기의 class에서 시작해서 더 큰 class로 올라가며 탐색하면 된다.
* 처음에는 "각 class 내부 first fit + 상위 class 순차 탐색"만 해도 구현이 단순하고 충분히 빠르다.
*
* - `coalesce`
* 이웃 free block이 있으면 각각 자기 class list에서 먼저 제거한 뒤 합친다.
* 그리고 최종 병합 블록 한 개만 새 크기에 맞는 class로 다시 insert해야 한다.
*
* - `place` / `place_allocated`
* 할당 전에 원래 free block은 해당 seglist에서 제거해야 한다.
* split이 생기면 남은 free block은 "남은 크기 기준 class"로 다시 insert해야 한다.
*
* - 필요하면 나중에 `find_fit` 안에서만 쓸 작은 helper를 하나 더 만들어도 된다.
* 예: "특정 class head를 가져오는 함수", "다음 non-empty class를 찾는 함수"
*/
static void *extend_heap(size_t words);
static void *coalesce(void *bp);
static char *find_fit(size_t size);
static void place(char *bp, size_t asize);
static void place_allocated(char *bp, size_t block_size, size_t asize, int coalesce_split);
static void push_free(void *p);
static void rem_free(void *p);
/*
* mm_init - malloc 패키지 초기화.
*/
int mm_init(void)
{
// (void*) -1
/**
* (void *)는 모든 타입을 저장할 수 있는 저장소 (주소만 알 수 있음)
* 즉, 어떤 타입이든 담을 수 있는 generic pointer
* 주소만 가지고 있고, 역참조하려면 캐스팅 필요
*
* (void*) -1 (유효하지 않은 주소)
* 보통 시스템 콜 실패 시 반환값!
*/
size_t prologue_size = SEG_ROOT_BYTES + DSIZE;
if ((heap_listp = mem_sbrk(SEG_ROOT_BYTES + (4 * WSIZE))) == (void *)-1)
return -1;
PUT(heap_listp, 0); // 4 byte 패딩 설정
PUT(heap_listp + (1 * WSIZE), PACK(prologue_size, 1));
seg_free_listp = heap_listp + (2 * WSIZE);
for (int i = 0; i < SEG_LIST_COUNT; i++)
{
SET_SEG_HEAD(i, NULL);
}
PUT(heap_listp + SEG_ROOT_BYTES + (2 * WSIZE), PACK(prologue_size, 1));
PUT(heap_listp + SEG_ROOT_BYTES + (3 * WSIZE), PACK(0, 1));
heap_listp = seg_free_listp;
if (extend_heap(CHUNKSIZE / WSIZE) == NULL)
return -1;
return 0;
}
static void *extend_heap(size_t words)
{
char *bp;
size_t size;
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
if ((bp = mem_sbrk(size)) == (void *)-1)
{
return NULL;
}
// 이거 때문에 초기화 할 때, 패딩과 프롤로그 헤더를 따로 만든다.
PUT(HDRP(bp), PACK(size, 0)); // free block header
PUT(FTRP(bp), PACK(size, 0)); // free block footer
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); // new epilogue header
// 힙을 늘릴 때, 앞에 있는 것이 Free일 수 도 있기 때문
// 힙 확장 전략을 고려한 것 (My Think 검증 아직 안함)
return coalesce(bp);
}
// heap_free_listp에 스택 방식으로 집어넣기
static void push_free(void *p)
{
if (p == NULL)
{
return;
}
// 1. 사이즈를 구한다.
size_t size = GET_SIZE(HDRP(p));
// 2. 사이즈에 맞는 클래스를 찾는다.
char index = SEG_INDEX(size);
// 3. LIFO 방식으로 집어넣는다.
char *old_ptr;
old_ptr = GET_SEG_HEAD(index);
/**
* PUT2(PRED(p), NULL);
* PUT2(SUCC(p), old_ptr);
*/
SET_PRED(p, NULL);
SET_SUCC(p, old_ptr);
if (old_ptr != NULL)
{
SET_PRED(old_ptr, p);
}
SET_SEG_HEAD(index, p);
}
// heap_free_listp에 스택 방식으로 빼기
// 만약에 rem_free할 공간을 찾질 못한다면?
static void rem_free(void *p)
{
// 1. 먼저 사이즈를 구한다.
if (p == NULL)
{
return;
}
size_t size = GET_SIZE(HDRP(p));
// 2. 사이즈에 맞는 클래스를 찾는다.
char index = SEG_INDEX(size);
// 3. LIFO 방식으로 클래스에서 찾아서 제거한다.
// -> 하지만 그 클래스에 아무것도 존재하지 않는다면
// -> 다음 블럭으로 넘어가서 체크를 해봐야 한다.
// 중요!!
if (index < SEG_LIST_COUNT)
{
char *pred = GET_PRED(p);
char *succ = GET_SUCC(p);
/**
* 만약 없어진게 첫번째꺼라면
* free list 첫번째에 값 집어넣기
**/
if (pred == NULL)
{
SET_SEG_HEAD(index, succ);
}
else
{
// 앞에 있는거의 뒤를 다음걸로 설정
SET_SUCC(pred, succ);
}
// 만약 마지막이 아니라면,
if (succ != NULL)
{
// 뒤에 있는거의 앞을 이전걸로 설정
SET_PRED(succ, pred);
}
// 중요!!
SET_PRED(p, NULL);
SET_SUCC(p, NULL);
return;
}
return;
}
static void *coalesce(void *bp)
{
char *prev_bp = PREV_BLKP(bp);
char *next_bp = NEXT_BLKP(bp);
size_t prev_alloc = GET_ALLOC(FTRP(prev_bp));
size_t next_alloc = GET_ALLOC(HDRP(next_bp));
size_t size = GET_SIZE(HDRP(bp));
// free 전략을 취할 때,
// 합치면서 heap_free_listp에 있는거 제거 후 다시 추가
// 여기서 나는 계속 rem_free(bp)를 해줬었는데,
// 아직 bp는 생각해보니 heap_free_listp에 들어가지 않은 상태였다.. 할필요가 없었다.. (crash 발생 원인..ㅜㅜ)
if (!prev_alloc)
{
rem_free(prev_bp);
size += GET_SIZE(HDRP(prev_bp));
bp = prev_bp;
}
if (!next_alloc)
{
rem_free(next_bp);
size += GET_SIZE(HDRP(next_bp));
}
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
push_free(bp);
return bp;
}
static void place_allocated(char *bp, size_t block_size, size_t asize, int coalesce_split)
{
size_t remain_size = block_size - asize;
char *split_ptr;
if (remain_size < MINBLOCKSIZE)
{
PUT(HDRP(bp), PACK(block_size, 1));
PUT(FTRP(bp), PACK(block_size, 1));
return;
}
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
split_ptr = NEXT_BLKP(bp);
PUT(HDRP(split_ptr), PACK(remain_size, 0));
PUT(FTRP(split_ptr), PACK(remain_size, 0));
// realloc처럼 allocated block 중간에서 split한 free block은 다음 블록과 바로 이어질 수 있으므로 coalesce 경로가 필요하다.
// mm_malloc에서 원래 free block을 잘라 쓰는 경우에는 이미 coalescing이 끝난 상태라 push_free만 해도 된다.
if (coalesce_split)
{
coalesce(split_ptr);
}
else
{
push_free(split_ptr);
}
}
/*
* mm_malloc - brk 포인터를 늘려 블록 할당.
* 항상 정렬 배수 크기의 블록을 할당한다.
*/
void *mm_malloc(size_t size)
{
size_t asize;
size_t extendsize;
char *bp;
if (size == 0)
return NULL;
/**
* explicit free list의 경우
* pred, succ가 8바이트씩 더 잡는다고 생각해서 무조건 32byte가 최소단위라고 생각했는데,
* 아니고 기존 payload 위치를 pred, succ가 대체한다고 생각해야함..
*
* size의 경우는 예제에 따라 4byte로 넣어도 상관없지만
* 주소는 64bit의 컴퓨터의 경우 환경에 따라 8byte로 주소가 들어갈 수 있으므로
*
* MINBLOCKSIZE = 24 byte
*/
asize = MAX(ALIGN(size + DSIZE), MINBLOCKSIZE);
// 할당 하기 전에 영역 전부를 다 살펴보고
// place를 통해 그 자리에 할당
if ((bp = find_fit(asize)) != NULL)
{
place(bp, asize);
return bp;
}
extendsize = MAX(asize, CHUNKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}
/**
* find_fit
*/
static char *find_fit(size_t size)
{
int index = SEG_INDEX(size);
while (index < SEG_LIST_COUNT)
{
char *bp = GET_SEG_HEAD(index);
while (bp != NULL)
{
// 안에서 Linked List 방식으로 탐색 중
size_t free_size = GET_SIZE(HDRP(bp));
if (!GET_ALLOC(HDRP(bp)) && free_size >= size)
{
return bp;
}
bp = GET_SUCC(bp);
}
index += 1;
}
return NULL;
}
static void place(char *p, size_t asize)
{
// 실제 사이즈
size_t real_size = GET_SIZE(HDRP(p));
rem_free(p);
place_allocated(p, real_size, asize, 0);
}
/*
* mm_free - 블록 해제 시 아무 동작도 하지 않음.
*/
void mm_free(void *ptr)
{
if (ptr == NULL)
{
return;
}
// *(size_t *)ptr = 0;
size_t size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
coalesce(ptr);
}
void *mm_realloc(void *ptr, size_t size)
{
// free alloc free
// 1. 사이즈가 감소할 경우 -> place로 자르기
// 2. 뒤에 free가 있고, 뒤에 사이즈를 더한 값이 size보다 클 경우에는 딱 충분한 크기만큼만 확장 (free일 경우) - place를 사용해서 구현
// 3. 뒤가 확장이 불가능한 eplilogue header일 경우
// 4. 이 경우들 다 빼고 예외상황에는 단순구현으로
if (ptr == NULL)
{
return mm_malloc(size);
}
if (size == 0)
{
mm_free(ptr);
return NULL;
}
void *newptr;
char *next_bp = NEXT_BLKP(ptr);
size_t asize = MAX(ALIGN(size + DSIZE), MINBLOCKSIZE); // 요청크기
size_t cur_size = GET_SIZE(HDRP(ptr));
size_t next_size = GET_SIZE((HDRP(next_bp)));
size_t copySize;
// 1. 요청 크기가 더 작아진 경우에는 같은 자리에서 바로 잘라서 남는 부분만 free block으로 돌려준다.
if (asize <= cur_size)
{
// 이걸 하는게 더 메모리 효율이 좋을까? 나쁠까? 분석
// place_allocated(ptr, cur_size, asize, 1);
return ptr;
}
// 1-1. malloc으로 공간있는지 먼저 확인
// realloc에서는 현재 allocated block 자체에 coalesce(ptr)를 직접 쓰면 안 된다.
// 2. 확장가능 alloc free
// 이전 free block과 합쳐지면 payload 시작 주소가 바뀔 수 있기 때문이다.
// 따라서 바로 뒤 free block만 free list에서 제거해서 현재 블록 뒤에 직접 붙인다.
if (!GET_ALLOC(HDRP(next_bp)) && (cur_size + next_size) >= asize)
{
rem_free(next_bp);
place_allocated(ptr, cur_size + next_size, asize, 1);
return ptr;
}
// 바로 뒤가 epilogue면 heap을 늘린 뒤, 다시 한 번 "뒤 free 붙이기"를 시도한다.
// 공간이 없으므로 추가
if (next_size == 0 || (!GET_ALLOC(HDRP(next_bp)) && (cur_size + next_size) < asize))
{
// 3. 확장불가능
/**
* -> [alloc free]이지만, alloc + free가 asize보다 작은 경우 -> [*alloc free alloc] -> [alloc alloc]으로, 확장 불가능한 경우
* -> [alloc epilogue]으로, 아예 heap 자체를 확장해야하는 경우 -> [alloc free epilogue]
**/
// MAX(asize - cur_size, CHUNKSIZE)
if (extend_heap((asize - cur_size) / WSIZE) != NULL)
{
// 2번째 케이스
next_bp = NEXT_BLKP(ptr);
next_size = GET_SIZE(HDRP(next_bp));
if (!GET_ALLOC(HDRP(next_bp)) && (cur_size + next_size) >= asize)
{
rem_free(next_bp);
place_allocated(ptr, cur_size + next_size, asize, 1);
return ptr;
}
}
}
// 위 경로가 모두 실패하면 새 블록을 받아서 옮기는 단순 fallback으로 처리한다.
// mm_malloc이 내부에서 find_fit/place를 모두 처리하므로 realloc에서는 free block을 직접 다루지 않는 편이 안전하다.
newptr = mm_malloc(size);
if (newptr == NULL)
{
return NULL;
}
// 복사 길이는 "기존 payload 크기"와 "새 요청 크기" 중 작은 값이어야 한다.
// block 전체 크기를 memcpy에 넣으면 header/footer까지 읽을 수 있다.
copySize = cur_size - DSIZE;
if (size < copySize)
{
copySize = size;
}
memcpy(newptr, ptr, copySize);
mm_free(ptr);
return newptr;
}
4. best fit 변경 (find_fit 부분만 변경)
더보기
static char *find_fit(size_t size)
{
int index = SEG_INDEX(size);
while (index < SEG_LIST_COUNT)
{
char *bp = GET_SEG_HEAD(index);
char *best_bp = NULL;
size_t best_size = 0;
while (bp != NULL)
{
size_t free_size = GET_SIZE(HDRP(bp));
if (!GET_ALLOC(HDRP(bp)) && free_size >= size)
{
if (free_size == size)
{
return bp;
}
if (best_bp == NULL || free_size < best_size)
{
best_bp = bp;
best_size = free_size;
}
}
bp = GET_SUCC(bp);
}
if (best_bp != NULL)
{
return best_bp;
}
index += 1;
}
return NULL;
}
5. binpolicy (find_fit 부분만 변경)
더보기
#define SMALL_FIT_CUTOFF 128
#define IS_WILDERNESS(bp) (GET_SIZE(HDRP(NEXT_BLKP(bp))) == 0)
static char *find_fit(size_t size)
{
int index = SEG_INDEX(size);
char *saved_top = NULL;
size_t saved_top_size = 0;
for (int i = index; i < SEG_LIST_COUNT; i++)
{
char *bp = GET_SEG_HEAD(i);
char *best_bp = NULL;
size_t best_size = 0;
while (bp != NULL)
{
size_t free_size = GET_SIZE(HDRP(bp));
if (free_size >= size)
{
if (size <= SMALL_FIT_CUTOFF && IS_WILDERNESS(bp))
{
if (saved_top == NULL || free_size < saved_top_size)
{
saved_top = bp;
saved_top_size = free_size;
}
bp = GET_SUCC(bp);
continue;
}
if (i == index)
{
return bp; // same-bin first-fit
}
if (best_bp == NULL || free_size < best_size)
{
best_bp = bp;
best_size = free_size;
}
}
bp = GET_SUCC(bp);
}
if (best_bp != NULL)
{
return best_bp;
}
}
return saved_top; // 다른 후보가 없을 때만 wilderness 사용
}
'정글캠프-WIL > 알고리즘' 카테고리의 다른 글
| 점프 (DP) (0) | 2026.04.02 |
|---|---|
| 동전 (DP) (0) | 2026.04.02 |
| LCS (DP) - Longest Common Subsequence (0) | 2026.04.02 |
| 평범한 배낭 (DP) (0) | 2026.04.02 |
| 동전 2 (BFS) (0) | 2026.03.26 |