Write malloc by yourself and malloc by yourself
Next, I wrote a blog post about malloc and sbrk in Process Memory Management in Linux. Let's talk about how to write the implementation details of the malloc function by yourself.
If you read the previous article, you should know the memory management of the process and the implementation principle of the malloc function. In fact, the space allocated by the malloc function is obtained by using the sbrk function, but this function should not be called every time the user calls malloc, which is too overhead and unnecessary. Therefore, you need to add memory allocation, release, merge, and other operations in malloc.
The first thing to note is that each time the space applied through the sbrk function is continuous, the memory space to be managed by the malloc function is also consecutive. However, as users release and apply for multiple times, some of these spaces are in the user Usage Status and some are in the user release status:
Metadata Design
As shown in, in each section of space, in addition to the space requested by the user, you also need a part of meta-data to manage the space. In my program, I set the size of this Part to 8 bytes. These 8 bytes not only need to store the size of the segment space, but also need to indicate the starting position of the next segment space. In my design, the first four bytes are used to indicate the length of the segment space. The length includes not only the length of the data Segment but also the 8 bytes of meta-data; the last four bytes indicate whether the segment space is in use or idle state.
Another important thing to note is alignment, because alignment allocation consumes less space and facilitates later management. In this program design, I use 8-byte alignment. For example, if a user applies for a 99-byte space, the space actually allocated to the user should be a factor greater than the minimum 8 of 99, that is, 104, in addition, 8 bytes of meta-data need to be added, so the total applied space is 112 bytes.
In other words, do not use division when calculating the size of the space actually allocated to the user. A more efficient way is to use shift operations. For example, if you need to apply for a size space of several bytes, you can use (size-1)> 3) <3) to calculate the actually allocated space) + 8 + 8.
In addition, it is required that sbrk should be used at least 8192 bytes at a time.
13 # define SPACE_IN_USE 0 // user usage 14 # define SPACE_AVAILABLE 1 // idle status 15 # define MINIMUM_SBRK_SPACE 8192 16 17 # define align8 (size) \ 18 size = (size-1)> 3) <3) + 8 25 void * free_list_head; // static variable, indicating the start address of the dynamic space
Another function of maintaining the space linked list malloc is to maintain the space opened through the sbrk. Through the metadata described above, it is not difficult to find that the memory space is actually a linked list, this linked list records each space, including the space that the user is using or is not using. To maintain this linked list, two function functions are required:
Void * free_list_begin (); // return the starting address of the first idle space. If no, return NULLvoid * free_list_next (void * node); // return the last space of the node, NULL <pre name = "code" class = "cpp"> void * <pre name = "code" class = "cpp"> free_list_begin
() 11 {12 // if the head is null 13 if (! Free_list_head) 14 {15 return NULL; 16} 17 else 18 {19 void * tmp = free_list_head; 20 while (tmp! = (Void *) sbrk (0) 21 {22 int * size = (int *) tmp; 23 int * flag = (int *) (tmp + 4 ); // the flag to indicate whether this space is in use 24 if (* flag = SPACE_AVAILABLE) 25 {26 return tmp; 27} 28 else 29 {30 tmp + = * size; 31} 32} 33 return NULL; 34} 35} 37 void * free_list_next (void * node) 38 {39 if (! Free_list_head |! Node) 40 {41 return NULL; 42} 43 44 if (node <free_list_head | node> sbrk (0) 45 {46 printf ("illeagal address \ n "); 47 return NULL; 48} 49 int flag = 0; 50 void * tmp = free_list_head; 51 while (tmp <sbrk (0) 52 {53 int * size = (int *) tmp; 54 if (tmp = node) 55 {56 tmp + = * size; 57 break; 58} 59 tmp + = * size; 60 61} 62 if (tmp = sbrk (0) 63 {64 return NULL; 65} 66 while (tmp <sbrk (0 )) 67 {68 int * size = (int *) tmp; 69 int * flag = (int *) (tmp + 4); 70 if (* flag) = SPACE_AVAILABLE) 71 {72 return tmp; 73} 74 tmp + = (* size); 75} 76 return NULL; 77}
My_malloc and my_free Functions
79 void* my_malloc_new(size_t size) 80 { 81 size_t new_size = size; 82 align8(new_size); 83 new_size += 8; 84 if(new_size >= MINIMUM_SBRK_SPACE) 85 { 86 void* res = sbrk(new_size); 87 if((void*)-1 == res) 88 { 89 printf("error occured when allocation space\n"); 90 return NULL; 91 } 92 *((int*)res) = new_size; 93 *((int*)(res + 4)) = SPACE_IN_USE; 94 return res; 95 } 96 else 97 { 98 size_t gap = MINIMUM_SBRK_SPACE - new_size; 99 align8(gap);100 gap+=8;101 void* res = sbrk(gap + new_size);102 if((void*)-1 == res)103 {104 printf("error occured when allocating space\n");105 return NULL;106 }107 *((int*)res) = new_size;108 *((int*)(res + 4)) = SPACE_IN_USE;109 110 res += new_size;111 *((int*)res) = gap;112 *((int*)(res + 4)) = SPACE_AVAILABLE;113 res -= new_size;114 return res;115 }116 117 118 }120 void* my_malloc(size_t size)121 {122 //create a new node123 if(NULL == free_list_begin())124 {125 void* res = my_malloc_new(size);126 free_list_head = res;127 return res + 8;128 }129 else130 {131 void* tmp = free_list_begin();132 133 size_t new_size = size;134 align8(new_size);135 new_size += 8;136 137 138 while(tmp != NULL)139 {140 int cur_size = *((int*)tmp);141 if(cur_size >= new_size)142 {143 if(cur_size >= new_size + 12)144 {145 *((int*)tmp) = new_size;146 *((int*)(tmp + 4)) = SPACE_IN_USE;147 148 void* new_tmp = tmp + new_size;149 *((int*)(new_tmp)) = cur_size - new_size;150 *((int*)(new_tmp + 4)) = SPACE_AVAILABLE;151 }152 else153 {154 *((int*)(tmp + 4)) = SPACE_IN_USE;155 }156 157 return tmp + 8;158 }159 tmp = free_list_next(tmp);160 }161 162 void* res = my_malloc_new(size);163 return res + 8;164 }165 }167 void my_free(void *ptr)168 {169 if(ptr < free_list_head || sbrk(0) < ptr)170 {171 printf("illegal pointer\n");172 return;173 }174 printf("free ptr:%p\n", ptr);175 int* flag = (int*)(ptr - 4);176 *flag = SPACE_AVAILABLE;177 return;178 }
The my_free function is simple, that is, to set the symbol position in meta-data to SPACE_AVAILABLE. The logic of my_malloc is to traverse the entire idle space through free_list_begin () and free_list_next (). If there is enough space, allocate it. If not, re-allocate it using sbrk.
Coalesce_free_list another problem to be aware of is that when the user uses my_malloc and my_free for multiple times, there will be a fragmentation problem, that is, there are multiple consecutive, free space, the coalesce_free_list function is used to merge these consecutive idle spaces into one space.
180 void coalesce_free_list()181 {182 void *first, *second, *tail;183 first = free_list_begin();184 int tmp_size = *((int*)first);185 int size = tmp_size;186 second = first + tmp_size;187 tail = sbrk(0);188 189 while(second < tail && first < tail)190 {191 while(second < tail && *((int*)(second + 4)) == SPACE_AVAILABLE )192 {193 tmp_size = *((int*)second);194 size += tmp_size;195 second += tmp_size;196 }197 *((int*)first) = size;198 first = second;199 while(first < tail && *((int*)(first + 4)) == SPACE_IN_USE )200 {201 first += *((int*)first);202 }203 if(first == tail)204 {205 return;206 }207 else208 {209 size = *((int*)first);210 second = first + size;211 }212 }213 return;214 }
All right, the entire code is finished. I have already put the code on csdn. You can download it: Click to open the link.