Header , Footer : 각각 블록의 시작과 끝을 나타내는 부분, 블록의 사이즈와 할당 여부를 저장한다.
Prev block pointer, Next Block pointer : 가용 블록들끼리 포인터들로 연결 시키기 위해 사용하는 블록 포인터 ( 이중 연결리스트 형태 )
Payload : 실제 정보들이 들어가는 부분
Padding : 8Bytes 단위를 맞추기 위해 할당해야 할 메모리보다 많은 메모리를 할당해줌 (선택적으로 사용한다.)
Header , Footer , Pointers 들은 각각 32 bit 운영 체제 기준 4 Bytes 이다.
Memory는 이런 블록들로 구성되어 Heap 속에 저장되어 있다.
블록 들을 저장하는 방법에 따라 Explicit Allocator , Implicit Allocator 두 가지 할당기로 나뉘게 된다.
여기에서는 C / C ++ 에서 사용하는 Explicit Allocator에 대해서 배워 보겠다.
Explicit Allocator
@ 응용 프로그램이 직접 메모리를 할당 / 반환하는 방식의 Allocator
@ 할당이 해제 된 가용 블록들을 연결리스트 형태로 관리 ( Prev , Next block pointer 사용 )
@ C 나 C++ 와 같은 low-level 언어에서 사용
연결 리스트에 블록을 추가하는 방법
1) Ordered List : 순서대로 추가
2) LIFO( Last-In-First-Out ) : Stack 처럼 작동
Ordered List는 자신의 순서를 연결리스트에서 찾아야 하므로 linear time이 걸림.
LIFO List의 경우 바로 집어넣으면 되기 때문에 Constant time이 걸린다.
일반적으로LIFO 방식이 성능 적인 면에서 우세 하다.
LIFO를 이용한 Explicit Allocator의 Heap 구조
0x08블록은 할당되었지만 연결리스트에 들어 있는 이유는 그저 연결리스트의 마지막을 나타내주기 위함이다.
새로운 가용 블록이 생기면 연결리스트 맨 앞에 붙게 된다.
이제 그럼 LIFO 방식의 Explicit Allocator를 구현 해보자
C++ 을 사용하여 구현 하였다.
맴버 변수
char* heap ; // Start point of Heap
char* brk; // End point of Heap
char* max_addr; // Max Address of Heap
char* free_list; // Pointing Front Free Block
매크로
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MACRO ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* @brief Macros for Size
*/
#define WSIZE 4 // 1 BYTE
#define DSIZE 8 // Double BYTE
#define MAX_HEAP 100 * (1<<20) // Maximum of HEAP 100 MB
#define CHUNKSIZE (1<<9) // Size for Heap increasement ( 256 Bytes )
/**
* @brief Macros for Block Size & Alloc
*/
#define PACK(size, alloc) ((size) | (alloc)) // Block Size( Multiple of 8. using 29 bit ) + IsAlloc( 000 : Free / 001 Alloc using 3 bit 0~7 )
#define GET(p) (*(unsigned int*)p) // From Pointer p, getting 'int information'( PACK(size, alloc) )
#define PUT(p,val) (*(unsigned int*)(p) = val) // Putting 'int value' to Pointer P
#define GET_SIZE(p) (GET(p) & (~0x7)) // Getting Block Size information
#define GET_ALLOC(p) (GET(p) & (0x1)) // Getting IsAllock information
/**
* @brief Macros for Block
*/
#define HDBP(bp) ((char *)bp - WSIZE) // Header Block of bp
#define FTBP(bp) ((char *)bp + GET_SIZE(HDBP(bp)) - DSIZE) // Footer Block of bp
#define NEXT_BLKP(bp) ((char*)(bp) + GET_SIZE(HDBP(bp))) // Next Block of bp
#define PREV_BLKP(bp) ((char*)(bp) - GET_SIZE( ((char *)(bp) - DSIZE ))) // Previous Block of bp
/**
* @brief Macros for free List Macros
*/
#define GET_PREV(bp) (*(void**)bp) // Prev Block Pointer of bp
#define GET_NEXT(bp) (*(void**)((char *)bp+WSIZE)) // Next Block Pointer of bp
#define PREV_A_TO_B(bp1,bp2) (*(void **)bp1 = bp2) // Connecting Prev BP of bp1 to Address which is pointed by bp2
#define NEXT_A_TO_B(bp1,bp2) (*(void **)((char*)bp1+WSIZE) = bp2) // Connecting Next BP of bp1 to Address which is pointed by bp2
매크로가 정말 많다.
PACK: Header 와 Footer에 필요한 Size , Alloc여부를 합쳐주는 매크로
GET , PET : void * 가 인자로 주어지므로 int 값을 얻기 위해 형 변환 진행
GET_SIZE , GET_ALLOC : PACK에서 각각 size 와 alloc 여부를 뽑아냄
HDBP , FTBP : 각각 Header와 Footer를 가리키는 pointer를 반환
PREV_BLKP , NEXT_BLKP : 각각 이전 , 다음 블록으로 이동하는 매크로
GET_PREV , GET_NEXT : 각각 PREV, NEXT pointer가 가리키는 block 반환
PREV_A_TO_B : A의 PREV pointer가 B가 가리키는 곳을 가리킴
NEXT_A_TO_B : A의 NEXT pointer가 B가 가리키는 곳을 가리킴
서브 함수들
Extend_heap , Mem_sbrk 함수
Heap을 늘려 주는 함수
mem_sbrk : heap 크기를 늘려줌
extend_heap : heap이 늘어남에 따라 블록 조정을 해줌
/**
* @brief The function that increases space of heap when it is lack
*
* @param words : Number of words ( word : 4 Bytes )
* @return void* : New Free block pointer
*/
void * Mymalloc::extend_heap (size_t words)
{
void *bp;
size_t size;
size = (words % 2) ? (words +1) * WSIZE : words * WSIZE; // Double Word (8) Alignment
if((bp = mem_sbrk(size)) == (void *) -1) return (void *)-1;
// Modifying header and footer
PUT(HDBP(bp),PACK(size , 0));
PUT(FTBP(bp) ,PACK(size , 0));
PUT(HDBP(NEXT_BLKP(bp)) , PACK(0,1)); // Moving Last block
return coalesce(bp); // If the previous block is not allocated, combine the two blocks
}
/**
* @brief The function that increases the size of the heap
*
* @param incr : Amount of increasement of heap
* @return void* : Brk pointer before increasement
*/
void* Mymalloc::mem_sbrk(int incr){
char* old_brk = brk;
if( (incr < 0) || ( (brk + incr) > max_addr) ){ // Error
perror("mem_sbrk failed.");
return (void *)-1;
}
brk += incr; // Heap Size increases
if(old_brk == heap) cout << "[ Heap Increasement ] " <<" Before : " << old_brk-heap << " Bytes " << " -> " <<" After : " << brk-heap << " Bytes " << " [ + " << incr <<" Bytes ] " << endl;
else cout << "[ Heap Increasement ] " <<" Before : " << old_brk-(heap - DSIZE) << " Bytes " << " -> " <<" After : " << brk-(heap - DSIZE) << " Bytes " << " [ + " << incr <<" Bytes ] " << endl;
return (void *)old_brk; // return previous brk
}
주석으로 설명을 많이 적어 두었다!!
Disconnect ,New_connect , Coalesce 함수
Disconnect : 가용 블록이 사용을 위해 할당되었을 때 가용 블록 연결 리스트에서 제거 하는 함수
New_connect : 새로 생긴 가용 블록을 가용 블록 연결 리스트에 LIFO 방식으로 연결하는 함수
Coalesce : 가용 블록이 생겼을 때 바로 옆에 가용 블록이 있다면 합치는 함수 ( 단편화를 줄이기 위함 )
/**
* @brief The function that disconnects the free block pointed by bp from the free list
*
* @param bp : Pointer which is pointing to use free block
*/
void Mymalloc::disconnect(void * bp){
void* prev = (void *)GET_PREV(bp);
void* next = (void *)GET_NEXT(bp);
if(prev) {
if(!next) NEXT_A_TO_B(prev,NULL);
else NEXT_A_TO_B(prev, next);
}
if(next){
if(!prev){ PREV_A_TO_B(next,NULL); free_list = (char *)next; }
else PREV_A_TO_B(next,prev);
}
PREV_A_TO_B(bp,NULL);
NEXT_A_TO_B(bp,NULL);
}
/**
* @brief The function that connects the free block pointed by bp to the free list (LIFO order).
*
* @param bp : pointer which is pointing new free block.
*/
void Mymalloc ::new_connect(void * bp){
PREV_A_TO_B(bp , NULL);
NEXT_A_TO_B(bp,free_list);
PREV_A_TO_B(free_list , bp);
free_list = (char *)bp;
}
/**
* @brief The function that combines the free blocks
*
* @param bp : Pointer which is pointing free block
* @return void* : Pointer which is pointing combined free block
*/
void * Mymalloc::coalesce(void *bp){
size_t size = GET_SIZE(HDBP(bp)); // Size of bp's block
size_t prev_alloc = GET_ALLOC(HDBP(PREV_BLKP(bp))); // Isalloc of Prev block of bp
size_t next_alloc = GET_ALLOC(HDBP(NEXT_BLKP(bp))); // Isallco if Next block of bp
// Case 0 (For realloc)
// Curr block is Allocated
// Next block is free block
if(GET_ALLOC(HDBP(bp)) == 1 && next_alloc == 0){
// Next block disconnect
disconnect(NEXT_BLKP(bp));
// Combining Curr block + Next block
size += GET_SIZE(HDBP(NEXT_BLKP(bp)));
PUT(HDBP(bp) , PACK(size , 1));
PUT(FTBP(bp) , PACK(size , 1));
return bp;
}
// Case 1
// Prev & Next block are allocated blocks
if(prev_alloc == 1 && next_alloc == 1){
// New Free block connect
new_connect(bp);
return bp;
}
// Case 2
// Prev block is free block
// Next block is allocated block
else if( prev_alloc == 0 && next_alloc == 1){
// Combining Block Prev block + Curr block
size += GET_SIZE(HDBP(PREV_BLKP(bp)));
PUT(HDBP(PREV_BLKP(bp)),PACK(size , 0));
PUT(FTBP(bp), PACK(size,0));
// prev free block disconnect
disconnect(PREV_BLKP(bp));
// New Free block connect
bp = PREV_BLKP(bp);
new_connect(bp);
}
// Case 3
// Prev block is allocated block
// Next block is free block
else if(prev_alloc == 1 && next_alloc == 0 ) {
// Next free block disconnect
disconnect(NEXT_BLKP(bp));
// Combining Block Curr block + Next block
size += GET_SIZE( HDBP(NEXT_BLKP(bp)));
PUT(HDBP(bp), PACK(size,0));
PUT(FTBP(bp) , PACK(size,0));
// New Free block connect
new_connect(bp);
}
// Case 4
// Prev & Next block are free block
else{
// Combining Block Prev block + Curr block + Next block
size += GET_SIZE(HDBP(PREV_BLKP(bp))) + GET_SIZE(HDBP(NEXT_BLKP(bp)));
PUT( HDBP(PREV_BLKP(bp)) , PACK(size , 0));
PUT( FTBP(NEXT_BLKP(bp)), PACK(size,0));
// Prev , Next free block disconnect
disconnect(PREV_BLKP(bp));
disconnect(NEXT_BLKP(bp));
// New Free block connect
bp = PREV_BLKP(bp);
new_connect(bp);
}
return bp;
}
주석 참고!!
Find_Fit , Place 함수
메모리 할당을 도와주는 함수
Find_fit : 사용하기에 알맞은 가용 블록을 찾아 주는 함수
Place : 가용 블록 할당 후 블록의 남은 부분을 다시 가용 블록으로 만들어 주는 함수
/**
* @brief The function that searches fit free block, in free list (LIFO order)
*
* @param asize : Demanded size
* @return void* : Pointer which is pointing fit free block. ( IF there isn't, return nullptr )
*/
void * Mymalloc::find_fit(size_t asize){
void* bp;
for(bp = (void *)free_list ; GET_ALLOC(HDBP(bp)) == 0 ; bp = GET_NEXT(bp) ){ // Finding list
if(GET_SIZE(HDBP(bp)) >= asize) return bp; // fit
}
return nullptr;
}
/**
* @brief The function that put the remaining block back in the free list again, when Free block is too large
*
* @param bp : Pointer which is pointing large free block
* @param asize : Size of large free block
*/
void Mymalloc::place(void *bp, size_t asize){
size_t csize = GET_SIZE(HDBP(bp));
if((csize - asize) >= 2 * DSIZE){
PUT(HDBP(bp) , PACK(asize , 1));
PUT(FTBP(bp) , PACK(asize , 1));
PUT(HDBP(NEXT_BLKP(bp)), PACK(csize-asize , 0));
PUT(FTBP(NEXT_BLKP(bp)), PACK(csize-asize , 0));
new_connect(NEXT_BLKP(bp));
}
else{
PUT(HDBP(bp) , PACK(csize , 1));
PUT(FTBP(bp) , PACK(csize, 1));
}
// Disconnecting allocated block from free list
disconnect(bp);
}