이번에는 Explicit List 방식으로 malloc / free 를 구현해보도록 하겠습니다. Introduction 에서 다뤘던 Explicit List 의 개념을 상기해보며 접근해봅시다.
Explicit List 방식으로 다룰 때 가장 핵심은 Free Block 을 관리하는 Doubled Linked List 가 있다고 생각하는 것입니다.
Free Block 중 가장 첫번째 가용 블럭은 prev로 NULL 을 가리키고 succ으로는 다음 free block 을 가리킵니다. 그리고 마지막 블럭의 succ 은 NULL을 가리킵니다.
*prev = previous, succ = succeeding
그리고 LIFO 방식으로 가용블럭을 manage 할 것을 기억하며 가용블럭을 사용할 때는 가장 첫번째 free block을 사용하고, free 를 통해 가용블럭을 추가할 때는 첫번째 block 이 되도록 해주어야합니다.
따라서 다음과 같은 메소드들을 정의해주었습니다.
*GET_PTR 는 매크로 함수로 prev, succ 에 저장한 이전 블럭, 다음 블럭의 주소를 char * 형태로 반환합니다.
#define GET_PTR(bp) ((char *)(GET(bp))
static void add_free_list(void *bp) {
if (first_block_pointer = NULL) {
first_block_pointer = bp;
} else {
PUT(SUCC(bp), first_block_pointer);
PUT(PRED(bp), NULL);
first_block_pointer = bp;
}
}
static void delete_from_free_list(void *bp) {
if (!GET_P(PRED(bp))) { // 삭제하고자 하는 free block 이 첫번째 free node라면.
if (!GET_P(SUCC(bp))) { // 삭제하고자 하는 free block 이 유일한 free block 이라면.
first_block_pointer = NULL;
return;
} else {
PUT(PRED(GET_P(SUCC(bp))), NULL); // 2번째 free block을 1번 free block으로 만들어 줌.
first_block_pointer = GET_P(SUCC(bp));
}
} else { // 삭제하고자 하는 free block 이 첫번째 free node 가 아니라면
PUT(SUCC(GET_P(PRED(bp))), GET_P(SUCC(bp)));
PUT(PRED(GET_P(SUCC(bp))), GET_P(PRED(bp)));
}
}
이 메소드들을 이용하면, free block 을 LIFO 방식의 Explicit List 로 Handling 할 수 있습니다.
이를 이용해 place, extend_heap, coalesce, free 도 적절하게 수정해줍니다.
place 함수만 부연 설명을 하자면, 사용할 가용블럭은 delete_from_free_list 메소드로 가용블럭 리스트에서 제거한 후 payload 할당 후 남은 block 은 add_free_list로 다시 가용블럭 리스트에 enroll 해줍니다.
다음 코드는 다음과 같습니다.
static void place(void *bp, size_t asize) {
//쪼개기 구현
size_t ThisBlockSize = GET_SIZE(HDRP(bp));
delete_from_free_list(bp);
if ( (ThisBlockSize-asize) >= 2 * DSIZE){ // 2*DSIZE 는 OVERHEAD와 최소payload 의 합과 같습니다.
PUT(HDRP(bp), PACK(asize,1));
PUT(FTRP(bp), PACK(asize,1));
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(ThisBlockSize-asize,0));
PUT(FTRP(bp), PACK(ThisBlockSize-asize,0));
add_free_list(bp);
}
else {
PUT(HDRP(bp), PACK(ThisBlockSize,1));
PUT(FTRP(bp), PACK(ThisBlockSize,1));
}
}
static void *coalesce(void *bp){
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))); // allocation status of previous block
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); // allocation status of next block
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && next_alloc){ // If two blocks are allocated already, return bp CASE 1
previous_heap_listp = bp;
return bp;
}
else if (prev_alloc && !next_alloc){ // If next block is free block, return extended block CASE 2
delete_from_free_list(bp);
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp), PACK(size,0));
add_free_list(bp);
}
else if(!prev_alloc && next_alloc){ // If previous block is free block, return extended block CASE 3
delete_from_free_list(bp);
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);
add_free_list(bp);
}
else { // If two blocks are free block, return extended block CASE 4
delete_from_free_list(bp);
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);
add_free_list(bp);
}
previous_heap_listp = bp; // for next fit
return bp;
}
static void *extend_heap(size_t words) {
void *bp;
size_t size;
size = (words%2) ? (words+1) * WSIZE : words * WSIZE; // adjust size even align
if ( (long)(bp = mem_sbrk(size)) == -1){
return NULL;
}
PUT(HDRP(bp), PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
PUT(HDRP(NEXT_BLKP(bp)), PACK(0,1)); // Set epilogue Block
PUT(PRED(bp), NULL);
PUT(SUCC(bp), NULL);
add_free_list(bp);
return coalesce(bp);
}
void free (void *ptr) {
if(!ptr) {
return;
}
size_t size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
PUT(PRED(ptr), NULL);
PUT(SUCC(ptr), NULL);
add_free_list(ptr);
coalesce(ptr);
}
마지막으로 explicit list 의 특성을 이용하여 find_fit 도 수정해주면 완성입니다!
static void *find_fit (size_t asize) {
void *bp;
for (bp = first_block_pointer; bp != NULL; bp = GET_P(SUCC(bp))) { // until meet Epilogue
if ( (!GET_ALLOC(HDRP(bp))) && (asize <= GET_SIZE(HDRP(bp))) ) {
return bp;
}
}
return NULL;
}
최종 소스코드는 다음과 같습니다.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mm.h"
#include "memlib.h"
/* If you want debugging output, use the following macro. When you hand
* in, remove the #define DEBUG line. */
#define DEBUG
#ifdef DEBUG
# define dbg_printf(...) printf(__VA_ARGS__)
#else
# define dbg_printf(...)
#endif
/* do not change the following! */
#ifdef DRIVER
/* create aliases for driver tests */
#define malloc mm_malloc
#define free mm_free
#define realloc mm_realloc
#define calloc mm_calloc
#endif /* def DRIVER */
#define WSIZE 4
#define DSIZE 8
#define CHUNKSIZE (1 << 12)
#define OVERHEAD 8
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define PACK(size, alloc) ((size) | (alloc))
#define GET(p) (*(unsigned int *)(p)) // unsigned int 반환
#define PUT(p, val) (*(unsigned int *)(p) = (val))
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
#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))
#define PRED(bp) ((char *)(bp))
#define SUCC(bp) ((char *)(bp) + WSIZE)
#define GET_PTR(bp) ((char *)GET(bp))
/* single word (4) or double word (8) alignment */
#define ALIGNMENT 8
/* rounds up to the nearest multiple of ALIGNMENT */
#define ALIGN(p) (((size_t)(p) + (ALIGNMENT-1)) & ~0x7)
/*copy from naive.c*/
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define SIZE_PTR(p) ((size_t*)(((char*)(p)) - SIZE_T_SIZE))
static void place(void *bp, size_t asize);
static void *coalesce(void *bp);;
static void *find_fit (size_t asize);
static void *extend_heap(size_t words);
static void add_free_list(void *bp);
static void delete_from_free_list(void *bp);
static char *heap_listp = 0; // pointer of first block
static char *previous_heap_listp = 0;
static char *first_block_pointer = NULL;
// Root Node 는 NULL, Tail Node 도 NULL.
static void add_free_list(void *bp) {
if (first_block_pointer = NULL) {
first_block_pointer = bp;
} else {
PUT(SUCC(bp), first_block_pointer);
PUT(PRED(bp), NULL);
first_block_pointer = bp;
}
}
static void delete_from_free_list(void *bp) {
if (!GET_PTR(PRED(bp))) { // 삭제하고자 하는 free block 이 첫번째 free node라면.
if (!GET_PTR(SUCC(bp))) { // 삭제하고자 하는 free block 이 유일한 free block 이라면.
first_block_pointer = NULL;
return;
} else {
PUT(PRED(GET_PTR(SUCC(bp))), NULL); // 2번째 free block을 1번 free block으로 만들어 줌.
first_block_pointer = GET_PTR(SUCC(bp));
}
} else { // 삭제하고자 하는 free block 이 첫번째 free node 가 아니라면
PUT(SUCC(GET_PTR(PRED(bp))), GET_PTR(SUCC(bp)));
PUT(PRED(GET_PTR(SUCC(bp))), GET_PTR(PRED(bp)));
}
}
static void place(void *bp, size_t asize) {
//쪼개기 구현
size_t ThisBlockSize = GET_SIZE(HDRP(bp));
delete_from_free_list(bp);
if ( (ThisBlockSize-asize) >= 2 * DSIZE){ // 2*DSIZE 는 OVERHEAD와 최소payload 의 합과 같습니다.
PUT(HDRP(bp), PACK(asize,1));
PUT(FTRP(bp), PACK(asize,1));
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(ThisBlockSize-asize,0));
PUT(FTRP(bp), PACK(ThisBlockSize-asize,0));
add_free_list(bp);
}
else {
PUT(HDRP(bp), PACK(ThisBlockSize,1));
PUT(FTRP(bp), PACK(ThisBlockSize,1));
}
};
static void *coalesce(void *bp){
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))); // allocation status of previous block
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); // allocation status of next block
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && next_alloc){ // If two blocks are allocated already, return bp CASE 1
previous_heap_listp = bp;
return bp;
}
else if (prev_alloc && !next_alloc){ // If next block is free block, return extended block CASE 2
delete_from_free_list(bp);
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp), PACK(size,0));
add_free_list(bp);
}
else if(!prev_alloc && next_alloc){ // If previous block is free block, return extended block CASE 3
delete_from_free_list(bp);
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);
add_free_list(bp);
}
else { // If two blocks are free block, return extended block CASE 4
delete_from_free_list(bp);
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);
add_free_list(bp);
}
previous_heap_listp = bp; // for next fit
return bp;
}
static void *extend_heap(size_t words) {
void *bp;
size_t size;
size = (words%2) ? (words+1) * WSIZE : words * WSIZE; // adjust size even align
if ( (long)(bp = mem_sbrk(size)) == -1){
return NULL;
}
PUT(HDRP(bp), PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
PUT(HDRP(NEXT_BLKP(bp)), PACK(0,1)); // Set epilogue Block
PUT(PRED(bp), NULL);
PUT(SUCC(bp), NULL);
add_free_list(bp);
return coalesce(bp);
}
/*
* Initialize: return -1 on error, 0 on success.
*/
int mm_init(void) {
if ( (heap_listp = mem_sbrk(4*WSIZE)) == -1 ) // 초기 empty heap 설정
return -1; // heap_listp = 새로 생성되는 heap 영역의 시작 주소
PUT(heap_listp, 0); // heap의 시작부분 할당
PUT(heap_listp + WSIZE, PACK(OVERHEAD, 1)); // prologue header block
PUT(heap_listp + DSIZE, PACK(OVERHEAD, 1)); // prologue footer block
PUT(heap_listp+WSIZE+DSIZE, PACK(0, 1)); // epilogue header block
heap_listp += DSIZE; // heap_listp 를 prologue footer block 에 locate.
previous_heap_listp = heap_listp; // 첫번째 extend_heap 후 가장 먼저 나타나는 가용블럭 포인터
if (extend_heap(4) == NULL) { // CHUNKSIZE Bytes 의 free block 을 생성. 초기 가용블럭을 생성합니다.
return -1;
}
return 0;
}
static void *find_fit (size_t asize) {
void *bp;
// First Fit
// for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) { // until meet Epilogue
// if ( (!GET_ALLOC(HDRP(bp))) && (asize <= GET_SIZE(HDRP(bp))) ) {
// return bp;
// }
// }
// return NULL;
// Using Explicit List
for (bp = first_block_pointer; bp != NULL; bp = GET_PTR(SUCC(bp))) { // until meet Epilogue
if ( (!GET_ALLOC(HDRP(bp))) && (asize <= GET_SIZE(HDRP(bp))) ) {
return bp;
}
}
return NULL;
// Next Fit
// for (bp = previous_heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) { // until meet Epilogue
// // printf("%x\n", bp);
// if ( (!GET_ALLOC(HDRP(bp))) && (asize <= GET_SIZE(HDRP(bp))) ) {
// return bp;
// }
// }
// void *bbp = previous_heap_listp;
// for (bp = heap_listp; bp < bbp; bp = NEXT_BLKP(bp)) { // until meet previous_heap_listp
// if ( (!GET_ALLOC(HDRP(bp))) && (asize <= GET_SIZE(HDRP(bp))) ) {
// return bp;
// }
// }
// return NULL;
}
/*
* malloc
*/
void *malloc (size_t size) {
size_t asize;
size_t extendsize;
char *bp;
// if (size < DSIZE) { //malloc 의 size 가 double words 보다 작을 경우
// asize = DSIZE + OVERHEAD;
// } else {
// asize = DSIZE * ( (size + 2*DSIZE - 1) / DSIZE );
// }
asize = ALIGN(size + OVERHEAD);
if ( (bp = find_fit(asize)) != NULL) { // asize 만큼 비어있는 가용블럭 (free block) 의 block pointer 찾기
place((void *)bp, asize);
previous_heap_listp = bp;
return (void *)bp;
} else { // find_fit 을 통해 가용블럭을 찾지 못한 경우, 힙을 증가시킵니다.
extendsize = MAX(asize,CHUNKSIZE);
if ( (bp = extend_heap(extendsize / WSIZE)) != NULL) { // CHUNKSIZE BYTES의 heap을 새로 생성합니다.
place((void *)bp, asize);
previous_heap_listp = bp;
return (void *)bp;
}
}
return NULL;
}
/*
* free
*/
void free (void *ptr) {
if(!ptr) {
return;
}
size_t size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
PUT(PRED(ptr), NULL);
PUT(SUCC(ptr), NULL);
add_free_list(ptr);
coalesce(ptr);
}
/*
* realloc - you may want to look at mm-naive.c
*/
void *realloc(void *oldptr, size_t size) {
size_t oldsize;
void *newptr;
/* If size == 0 then this is just free, and we return NULL. */
if(size == 0) {
free(oldptr);
return 0;
}
/* If oldptr is NULL, then this is just malloc. */
if(oldptr == NULL) {
return malloc(size);
}
newptr = malloc(size);
/* If realloc() fails the original block is left untouched */
if(!newptr) {
return 0;
}
/* Copy the old data. */
oldsize = *SIZE_PTR(oldptr);
if(size < oldsize) oldsize = size;
memcpy(newptr, oldptr, oldsize);
/* Free the old block. */
free(oldptr);
return newptr;
}
/*
* calloc - you may want to look at mm-naive.c
* This function is not tested by mdriver, but it is
* needed to run the traces.
*/
void *calloc (size_t nmemb, size_t size) {
size_t bytes = nmemb * size;
void *newptr;
newptr = malloc(bytes);
memset(newptr, 0, bytes);
return newptr;
}
/*
* Return whether the pointer is in the heap.
* May be useful for debugging.
*/
static int in_heap(const void *p) {
return p < mem_heap_hi() && p >= mem_heap_lo();
}
/*
* Return whether the pointer is aligned.
* May be useful for debugging.
*/
static int aligned(const void *p) {
return (size_t)ALIGN(p) == (size_t)p;
}
/*
* mm_checkheap
*/
void mm_checkheap(int verbose) {
}
Explicit List 구현 결과는 다음과 같습니다.
'시스템 프로그래밍' 카테고리의 다른 글
[System Programming] Vi Editor 명령어 (0) | 2022.12.19 |
---|---|
[System Programming] Linux 명령어 (0) | 2022.12.18 |
[Malloc Lab] Implicit List (0) | 2022.12.04 |
[Malloc Lab] Naive List (0) | 2022.12.04 |
[Malloc Lab] Introduction to Malloc Lab (0) | 2022.12.04 |