/*
* mm-naive.c - The fastest, least memory-efficient malloc package.
*
* In this naive approach, a block is allocated by simply incrementing
* the brk pointer. A block is pure payload. There are no headers or
* footers. Blocks are never coalesced or reused. Realloc is
* implemented directly using mm_malloc and mm_free.
*
* NOTE TO STUDENTS: Replace this header comment with your own header
* comment that gives a high level description of your solution.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include "mm.h"
#include <limits.h>
#include "memlib.h"
/*********************************************************
* NOTE TO STUDENTS: Before you do anything else, please
* provide your team information in the following struct.
********************************************************/
team_t team = {
/* Team name */
"ateam",
/* First member's full name */
"Harry Bovik",
/* First member's email address */
"bovik@cs.cmu.edu",
/* Second member's full name (leave blank if none) */
"",
/* Second member's email address (leave blank if none) */
""
};
/* single word (4) or double word (8) alignment */
#define ALIGNMENT 8
/* rounds up to the nearest multiple of ALIGNMENT */
#define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7)
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
/* Basic constants and macros */
#define WSIZE 4 /* Word and header/footer size (bytes) */
#define DSIZE 8 /* Double word size (bytes) */
#define CHUNKSIZE (1<<12) /* Extend heap by this amount (bytes) */
#define MAX(x, y) ((x) > (y)? (x) : (y))
/* Pack a size and allocated bit into a word */
#define PACK(size, alloc) ((size) | (alloc))
/* Read and write a word at address p */
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
/* Read the size and allocated fields from address p */
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
/* Given block ptr bp, compute address of its header and footer */
#define HDRP(bp) ((char *)(bp) - WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
/* Given block ptr bp, compute address of next and previous blocks */
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
/* Pointers to predecessor and successor in free list */
#define PREDP(bp) (*(void **)(bp))
#define SUCCP(bp) (*(void **)(bp + WSIZE))
/* --- Segregated List Defines --- */
#define NUM_CLASSES 20 /* Number of size classes */
typedef void * (*fit_policy_t)(size_t asize);
/* --- Global Variables --- */
static char *free_lists[NUM_CLASSES]; /* Array of segregated list heads */
static char *heap_listp; /* Pointer to prologue block */
/* --- Function Prototypes --- */
static void *coalesce(void *bp);
static void *extend_heap(size_t words);
static void *find_fit(size_t asize, fit_policy_t fit_policy);
static void *segregated_first_fit(size_t asize);
static void place(void *bp, size_t asize);
static void list_add(void *bp);
static void list_remove(void *bp);
static int get_list_index(size_t size);
/*
* mm_init - initialize the malloc package.
*/
int mm_init(void) {
if ((heap_listp = mem_sbrk(4 * WSIZE)) == (void *) -1)
return -1;
PUT(heap_listp, 0); /* Alignment padding */
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);
/* Initialize all segregated list heads to NULL */
for (int i = 0; i < NUM_CLASSES; i++) {
free_lists[i] = NULL;
}
if (extend_heap(CHUNKSIZE / WSIZE) == NULL)
return -1;
return 0;
}
/*
* mm_malloc - Allocate a block.
*/
void *mm_malloc(size_t size) {
size_t asize; /* Adjusted block size */
size_t extendsize; /* Amount to extend heap if no fit */
char *bp;
/* Ignore spurious requests */
if (size == 0)
return NULL;
/* Adjust block size to include overhead and alignment reqs. */
if (size <= DSIZE)
asize = 2 * DSIZE;
else
asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
/* Search the free list for a fit */
if ((bp = find_fit(asize, segregated_first_fit)) != NULL) {
place(bp, asize);
return bp;
}
/* No fit found. Get more memory and place the block */
extendsize = MAX(asize, CHUNKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}
/*
* mm_free - Freeing a block.
*/
void mm_free(void *bp) {
size_t size = GET_SIZE(HDRP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
coalesce(bp);
}
/*
* mm_realloc - Implemented simply in terms of mm_malloc and mm_free
*/
void *mm_realloc(void *ptr, size_t size) {
void *oldptr = ptr;
void *newptr;
if (size == 0) {
mm_free(ptr);
return NULL;
}
if (ptr == NULL) {
return mm_malloc(size);
}
newptr = mm_malloc(size);
if (newptr == NULL)
return NULL;
size_t copysize = GET_SIZE(HDRP(ptr)) - DSIZE;
if (size < copysize) {
copysize = size;
}
memcpy(newptr, oldptr, copysize);
mm_free(oldptr);
return newptr;
}
/*
* extend_heap - Extends the heap with a new free block.
*/
static void *extend_heap(size_t words) {
char *bp;
size_t size;
/* Allocate an even number of words to maintain alignment */
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
if ((long) (bp = mem_sbrk(size)) == -1)
return NULL;
/* Initialize free block header/footer and the epilogue header */
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 */
/* Coalesce if the previous block was free */
return coalesce(bp);
}
/*
* list_add - Add a free block to the appropriate size class list.
* Uses LIFO (Last-In, First-Out) insertion.
*/
static void list_add(void *bp) {
size_t size = GET_SIZE(HDRP(bp));
int index = get_list_index(size);
void *list_head = free_lists[index];
PREDP(bp) = NULL;
SUCCP(bp) = list_head;
if (list_head != NULL) {
PREDP(list_head) = bp;
}
free_lists[index] = bp;
}
/*
* list_remove - Remove a block from its size class list.
*/
static void list_remove(void *bp) {
size_t size = GET_SIZE(HDRP(bp));
int index = get_list_index(size);
void *pred_bp = PREDP(bp);
void *succ_bp = SUCCP(bp);
if (pred_bp == NULL && succ_bp == NULL) {
/* Only block in list */
free_lists[index] = NULL;
} else if (pred_bp == NULL) {
/* Head of list */
PREDP(succ_bp) = NULL;
free_lists[index] = succ_bp;
} else if (succ_bp == NULL) {
/* Tail of list */
SUCCP(pred_bp) = NULL;
} else {
/* Middle of list */
SUCCP(pred_bp) = succ_bp;
PREDP(succ_bp) = pred_bp;
}
}
/*
* coalesce - Merge adjacent free blocks.
*/
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) {
/* Case 1: Both allocated. No merge. */
} else if (prev_alloc && !next_alloc) {
/* Case 2: Next block is free. */
list_remove(NEXT_BLKP(bp));
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) {
/* Case 3: Previous block is free. */
list_remove(PREV_BLKP(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);
} else {
/* Case 4: Both previous and next are free. */
list_remove(PREV_BLKP(bp));
list_remove(NEXT_BLKP(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 the (potentially new, larger) block to the correct list */
list_add(bp);
return bp;
}
/*
* segregated_first_fit - Find a fit in the segregated lists.
* Searches from the block's size class up to larger classes.
*/
static void *segregated_first_fit(size_t asize) {
int index = get_list_index(asize);
void *bp;
/* Search from the correct size class upwards */
for (int i = index; i < NUM_CLASSES; i++) {
for (bp = free_lists[i]; bp != NULL; bp = SUCCP(bp)){
if (asize <= GET_SIZE(HDRP(bp))) {
return bp;
}
}
}
return NULL; /* No fit found */
}
/*
* find_fit - Wrapper for the chosen fit policy.
*/
static void *find_fit(size_t asize, fit_policy_t fit_policy) {
return fit_policy(asize);
}
/*
* place - Place block in heap, splitting if large enough.
*/
static void place(void *bp, size_t asize) {
size_t csize = GET_SIZE(HDRP(bp));
list_remove(bp); /* Remove this block from its free list */
if ((csize - asize) >= (2 * DSIZE)) {
/* Split the block */
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
bp = NEXT_BLKP(bp);
/* Add the remaining fragment back to the free list */
PUT(HDRP(bp), PACK(csize-asize, 0));
PUT(FTRP(bp), PACK(csize-asize, 0));
list_add(bp);
} else {
/* Do not split */
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
}
}
/*
* get_list_index - Given a block size, return the correct
* size class list index.
*/
static int get_list_index(size_t size) {
/* * Simple power-of-2 size classes.
* 0: [1, 32]
* 1: [33, 64]
* 2: [65, 128]
* ...
* 19: [2^20+1, infinity]
*/
if (size <= 32) return 0;
if (size <= 64) return 1;
if (size <= 128) return 2;
if (size <= 256) return 3;
if (size <= 512) return 4;
if (size <= 1024) return 5;
if (size <= 2048) return 6;
if (size <= 4096) return 7;
if (size <= 8192) return 8;
if (size <= 16384) return 9;
if (size <= 32768) return 10;
if (size <= 65536) return 11;
if (size <= 131072) return 12;
if (size <= 262144) return 13;
if (size <= 524288) return 14;
if (size <= 1048576) return 15;
if (size <= 2097152) return 16;
if (size <= 4194304) return 17;
if (size <= 8388608) return 18;
/* All larger blocks go in the last list */
return 19;
}
C 언어는 비지역 점프(nonlocal jump)라는 사용자 수준의 예외적 제어 흐름(ECF)을 제공합니다. 이는 일반적인 함수 호출/반환 순서를 무시하고, 한 함수에서 스택 상의 다른 (현재 실행 중인) 함수로 제어권을 직접 이전시키는 기능입니다. 이 기능은 setjmp와 longjmp 함수를 통해 제공됩니다.
setjmp와 longjmp의 동작 원리setjmp / sigsetjmp#include <setjmp.h>
int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);
env 버퍼에 저장합니다.setjmp는 여러 번 반환될 수 있습니다.longjmp를 통해 복귀 시: longjmp가 전달한 0이 아닌 값(retval)을 반환합니다.setjmp의 반환 값은 rc = setjmp(env);처럼 변수에 할당해서는 안 되며, switch나 if 조건문에서만 안전하게 사용할 수 있습니다.longjmp / siglongjmp#include <setjmp.h>
void longjmp(jmp_buf env, int retval);
void siglongjmp(sigjmp_buf env, int retval);
env 버퍼에 저장된 환경을 복원하고, 가장 최근에 env를 저장했던 setjmp 호출 지점으로 즉시 점프합니다.longjmp는 longjmp를 호출한 위치로 절대 반환하지 않습니다(Never returns).longjmp의 가장 중요한 용도는, 깊게 중첩된 함수(main foo bar)에서 에러가 발생했을 때, 스택을 일일이 return으로 거슬러 올라가지 않고 에러 핸들러로 즉시 탈출하는 것입니다.

main이 setjmp(buf)를 호출하여 복귀 지점(case 0)을 설정합니다.main이 foo()를 호출하고, foo()가 bar()를 호출합니다.bar()에서 error2가 감지되면, longjmp(buf, 2)를 호출합니다.foo()를 건너뛰고 main의 setjmp 위치로 즉시 점프합니다.setjmp는 longjmp가 보낸 2를 반환하고, switch문이 case 2를 실행하여 에러를 처리합니다.
longjmp는 시그널 핸들러가 중단된 코드로 복귀하는 대신, 프로그램의 특정 지점(예: 시작점)으로 분기할 수 있게 합니다. (예: Ctrl+C로 프로그램 재시작)
main이 sigsetjmp로 재시작 지점을 저장합니다. Ctrl+C (SIGINT) 시그널이 발생하면, 핸들러가 siglongjmp를 호출하여 main의 시작 지점으로 제어를 되돌려 프로그램을 "재시작"합니다.siglongjmp 사용 시 중요 주의사항경합 상태 (Race Condition):
반드시 sigsetjmp를 먼저 호출하여 복귀 지점을 설정한 후에, Signal로 핸들러를 설치해야 합니다. (그렇지 않으면 핸들러가 sigsetjmp보다 먼저 실행될 수 있음)
비동기-시그널-안전 (Async-Signal-Unsafe):
siglongjmp는 시그널-안전 함수가 아닙니다. 따라서 siglongjmp가 되돌아간 지점(즉, sigsetjmp가 0이 아닌 값을 반환한 직후)부터는 sio_puts나 sleep처럼 시그널-안전이 보장된 함수들만 호출해야 합니다.
리눅스 시스템은 프로세스를 모니터링하고 조작하기 위한 여러 유용한 도구들을 제공합니다.
strace:static 플래그와 함께 컴파일하는 것이 좋습니다.ps:top:pmap:/proc:cat /proc/loadavg를 입력하면, 리눅스 시스템의 현재 "로드 애버리지(load average)"를 볼 수 있습니다.setjmp, longjmp)를 사용하여 일반적인 호출/반환 스택 규율을 우회하고 한 함수에서 다른 함수로 직접 분기(branch)할 수 있습니다.