segregated free list의 구조와 방식
일단 겁먹지 말자. segregated free list는 explict free list에서 사용한 pred, succ의 로직을 그대로 사용한다.
앞서 살펴본 두 개의 방식(implicit free list, explicit free list)을 사용하는 할당기는 한 개의 블록을 할당할 때 free block의 수에 비례하는 시간이 필요하다.
이걸 획기적으로 줄이는 방법이 segregated free list방식이다.
어떻게 시간을 줄이냐?
다수의 가용 리스트를 유지해서 각 리스트에 비슷한 크기의 블록들을 저장하는 방식을 취한다.
할당기는 free list의 배열을 관리하고(위 그림에서 segregated list) 크기 클래스마다 크기가 증가하는 오름차순 순서로 하는 연결 리스트들을 가진다.
위에서 보면 끝의 자리수가 2의 배수로 분리시켜놨는데, 이건 분리를 시키는 하나의 방법이다.
- 이 방법이 좋은 점은 bitwise 연산을 이용해서 빠르게 요청된 block의 크기에 맞는 free list를 찾을 수 있다는 점이다(코드에서 어떻게 했는지 찾을 수 있다).
segregated list을 사용하고, 각 연결리스트마다 오름차순으로 정렬하고, first fit 정책을 적용하면 best fit을 사용한 것처럼 동작한다.
- 오름차순에서 first fit을 수행하면 내가 들어갈 수 있는 가장 적합한 free block에 들어가게 되기 때문이다.
- 이는 전체 힙을 best fit 검색하는 것을 단순화 했다고 볼 수 있다.
/*
* 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 "memlib.h"
/*********************************************************
* NOTE TO STUDENTS: Before you do anything else, please
* provide your team information in the following struct.
********************************************************/
team_t team = {
/* Team name */
"team 8",
/* First member's full name */
"D_corn",
/* First member's email address */
"dcron@jungle.com",
/* 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 // 워드 = 헤더 = 풋터 사이즈(bytes)
#define DSIZE 8 // 더블워드 사이즈(bytes)
#define CHUNKSIZE (1<<12) // heap을 이정도 늘린다(bytes)
#define LISTLIMIT 20 //list의 limit 값을 설정해준다. CHUNKSIZE에 비해 충분히 큰 값을 준 것 같다(정확한 이유 모르겠음)
#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 wirte a word at address p
//p는 (void*)포인터이며, 이것은 직접 역참조할 수 없다.
#define GET(p) (*(unsigned int *)(p)) //p가 가리키는 놈의 값을 가져온다
#define PUT(p,val) (*(unsigned int *)(p) = (val)) //p가 가리키는 포인터에 val을 넣는다
// Read the size and allocated fields from address p
#define GET_SIZE(p) (GET(p) & ~0x7) // ~0x00000111 -> 0x11111000(얘와 and연산하면 size나옴)
#define GET_ALLOC(p) (GET(p) & 0x1) // 할당이면 1, 가용이면 0
// 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
// 현재 bp에서 WSIZE를 빼서 header를 가리키게 하고, header에서 get size를 한다.
// 그럼 현재 블록 크기를 return하고(헤더+데이터+풋터), 그걸 현재 bp에 더하면 next_bp나옴
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
#define PRED_FREE(bp) (*(void**)(bp))
#define SUCC_FREE(bp) (*(void**)(bp + WSIZE))
static void *heap_listp;
static void *segregation_list[LISTLIMIT];
static void *extend_heap(size_t words);
static void *coalesce(void *bp);
static void *find_fit(size_t asize);
static void place(void *bp, size_t asize);
static void remove_block(void *bp);
static void insert_block(void *bp, size_t size);
/*
* mm_init - initialize the malloc package.
*/
int mm_init(void)
{
int list;
for (list = 0; list < LISTLIMIT; list++) {
segregation_list[list] = NULL; // segregation_list를 NULL로 초기화
} // 나중에 그 list에는 값이 없음을 표시할 수 있도록.
/* Create the initial empty heap */
if ((heap_listp = mem_sbrk(4 * WSIZE)) == (void *)-1)
{
return -1;
}
PUT(heap_listp, 0); // Unused padding
PUT(heap_listp + (1 * WSIZE), PACK(DSIZE, 1)); // 프롤로그 헤더 8/1
PUT(heap_listp + (2 * WSIZE), PACK(DSIZE, 1)); // 프롤로그 풋터 8/1
PUT(heap_listp + (3 * WSIZE), PACK(0, 1)); // 에필로그 헤더 0/1
heap_listp = heap_listp + 2*WSIZE; //왜 프롤로그 사이를 가리키고 있지?
//find_fit 함수에서 find를 할 때 사용하기 위해서
/* Extended the empty heap with a free block of CHUNKSIZE bytes */
if (extend_heap(CHUNKSIZE / WSIZE) == NULL)
{
return -1;
}
return 0;
}
/*
* mm_malloc - Allocate a block by incrementing the brk pointer.
* Always allocate a block whose size is a multiple of the alignment.
*/
void *mm_malloc(size_t size)
{
int asize = ALIGN(size + SIZE_T_SIZE);
// size_t asize; /* Adjusted block size */
size_t extendsize; /* Amount to extend heap if no fit */
char *bp;
/* Search the free list for a fit */
if ((bp = find_fit(asize)) != NULL) // 가용 블록을 찾을 수 있다면
{
place(bp, asize); // place 함수를 실행시킴
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 does nothing.
*/
void mm_free(void *ptr)
{
size_t size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
coalesce(ptr);
}
/*
* 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;
size_t copySize;
newptr = mm_malloc(size);
if (newptr == NULL)
return NULL;
copySize = GET_SIZE(HDRP(oldptr));
if (size < copySize)
copySize = size;
// memcpy - 메모리의 특정한 부분으로부터 얼마까지의 부분을 다른 메모리 영역으로
// 복사해주는 함수(oldptr로부터 copySize만큼의 문자를 newptr로 복사해라)
memcpy(newptr, oldptr, copySize);
mm_free(oldptr);
return newptr;
}
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 */
/* Coalesce if the previous block was free */
return coalesce(bp);
}
static void *coalesce(void *bp)
{
//coalesce는 succ, pred를 보는 것이 아니라 인접한 prev, next 블록을 보는 것을 주의!!!!!!!!!!
//전 블록 가용한지
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){ // 앞,뒤 모두 allocated
insert_block(bp,size);
return bp;
}
else if (prev_alloc && !next_alloc) // 앞은 allocated, 바로 뒤에 free block이 존재할 때
{
remove_block(NEXT_BLKP(bp)); // 뒤 블록을 일단 지워(나중에 합쳐서 insert 해줄거야)
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) // 앞은 free block, 뒤는 allocated
{
remove_block(PREV_BLKP(bp)); // 앞 블록을 일단 지워(나중에 합체하고 insert 해줄거야)
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); // 앞으로 bp 옮겨줘야함
}
else if (!prev_alloc && !next_alloc) // 앞과 뒤 모두 free block일 경우
{
remove_block(PREV_BLKP(bp)); // 다 지워
remove_block(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); // 앞으로 bp 옮겨줘야함
}
insert_block(bp, size); // 드디어 insert! (coalesce를 하면 무조건 insert가 실행됨)
return bp;
}
static void place(void *bp, size_t asize)
{
size_t csize = GET_SIZE(HDRP(bp));
// allocate된 블록은 freelist에서 지운다.
remove_block(bp);
// 필요한 블록 이외에 남는게 16바이트 이상이면 - (header,pred,succ,footer 각각 16byte필요)
if ((csize - asize) >= (2 * DSIZE)) //분할을 진행한다.
{ // 일단 할당블록 처리
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
bp = NEXT_BLKP(bp); //bp를 다음 블록으로 옮김
// 나머지 사이즈를 free시킨다.
PUT(HDRP(bp), PACK(csize - asize, 0));
PUT(FTRP(bp), PACK(csize - asize, 0));
coalesce(bp); // 이때 연결되어 있는 게 있을 수 있으므로 coalesce진행
} // coalesce 함수에 들어가면 무조건 insert를 하게 됨
else
{
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
}
}
static void *find_fit(size_t asize)
{
/* First-fit search */
void *bp;
int list = 0;
size_t searchsize = asize;
while (list < LISTLIMIT){
// (list가 현재 0~19이므로)가용블록을 못찾아서 19번째 리스트에 도달하거나,
// (19번째 list에는 무조건 넣어야 함)
// 나보다 큰 사이즈의 segregation_list가 NULL이 아니면 (나보다 큰 사이즈의 list 안에 free 블록이 존재할 경우)
if ((list == LISTLIMIT-1) || (searchsize <= 1)&&(segregation_list[list] != NULL)){
bp = segregation_list[list];
while ((bp != NULL) && (asize > GET_SIZE(HDRP(bp)))){
bp = SUCC_FREE(bp);
}
if (bp != NULL){
return bp;
}
}
searchsize >>= 1;
list++;
}
return NULL; /* no fit */
// #endif
}
static void remove_block(void *bp){
int list = 0;
size_t size = GET_SIZE(HDRP(bp));
while ((list < LISTLIMIT - 1) && (size > 1)) { //지우고자 하는 list idx 찾아들어감
size >>= 1;
list++;
}
if (SUCC_FREE(bp) != NULL){ // succ 블록이 NULL이 아니면
if (PRED_FREE(bp) != NULL){ // pred 블록이 NULL이 아니면 (중간에 있는걸 지우는 경우)
PRED_FREE(SUCC_FREE(bp)) = PRED_FREE(bp);
SUCC_FREE(PRED_FREE(bp)) = SUCC_FREE(bp);
}else{ // pred 블록이 NULL일 경우 (list에서 맨 처음을 지우는 경우)
PRED_FREE(SUCC_FREE(bp)) = NULL;
segregation_list[list] = SUCC_FREE(bp);
}
}else{ //succ 블록이 NULL일 경우
if (PRED_FREE(bp) != NULL){ //리스트의 끝의 블록을 지우는 경우
SUCC_FREE(PRED_FREE(bp)) = NULL;
}else{ // 애초에 하나만 존재했을 경우
segregation_list[list] = NULL;
}
}
return;
}
static void insert_block(void *bp, size_t size){
int list = 0;
void *search_ptr;
void *insert_ptr = NULL; //search_ptr의 값을 저장해놓는 용도(insert_ptr의 부모같음)
while ((list < LISTLIMIT - 1) && (size > 1)){ //segregation_list의 idx를 찾는 과정
size >>=1;
list++;
}
search_ptr = segregation_list[list];
//오름차순으로 저장하기 위해 나보다 작은 놈들은 넘기고 나보다 큰놈 앞에서 멈추게 됨
while ((search_ptr != NULL) && (size > GET_SIZE(HDRP(search_ptr)))){
insert_ptr = search_ptr;
search_ptr = SUCC_FREE(search_ptr); //succ로 계속 넘어가서 찾는다.
}
if (search_ptr != NULL){ //search_ptr이 NULL이 아닐 때
if (insert_ptr != NULL){ //insert_ptr이 NULL이 아닐 때
SUCC_FREE(bp) = search_ptr; //insert, search 사이에 넣는 경우
PRED_FREE(bp) = insert_ptr;
PRED_FREE(search_ptr) = bp;
SUCC_FREE(insert_ptr) = bp;
}else{ //insert_ptr이 NULL일 때 (안에 들어왔는데 내가 제일 작아서 list에 바로 삽입할 때)
SUCC_FREE(bp) = search_ptr;
PRED_FREE(bp) = NULL;
PRED_FREE(search_ptr) = bp;
segregation_list[list] = bp; //segregation_list 최신화
}
}else{ // search_ptr이 NULL일 때
if (insert_ptr != NULL){ // 처음 시작할 때는 이 코드가 돌아갈 일이 없지만
SUCC_FREE(bp) = NULL; // 진행하다보면 연결 list안에서 내가 제일 커서 search_ptr은 null,
PRED_FREE(bp) = insert_ptr; // insert_ptr은 현재 list에서 가장 큰 경우가 존재한다.
SUCC_FREE(insert_ptr) = bp;
}else{ // 아무것도 없어서 list에 내가 처음 넣을 때
SUCC_FREE(bp) = NULL;
PRED_FREE(bp) = NULL;
segregation_list[list] = bp; //segregation_list 최신화
}
}
return;
}
'sw사관학교정글' 카테고리의 다른 글
[week07] 웹 서버 만들기 (tiny server) (0) | 2021.12.27 |
---|---|
[week07] 네트워크 프로그래밍 (0) | 2021.12.27 |
[week06] 명시적 할당기 구현 - explicit free list 방식 (0) | 2021.12.17 |
[week06] 명시적 할당기 구현 - implicit free list 방식 (1) | 2021.12.17 |
[week06] 동적 메모리 할당 (1) | 2021.12.16 |