-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmyalloc.c
354 lines (306 loc) · 9.39 KB
/
myalloc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/**
* @author 170011474
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/mman.h>
#include "myalloc.h"
#include <pthread.h>
#include <unistd.h>
#ifndef MIN_ALLOC_UNIT
#define MIN_ALLOC_UNIT 3 * sysconf(_SC_PAGESIZE) // 3 pages
#endif
#ifndef STATUS_FREE
#define STATUS_FREE 1
#endif
#ifndef STATUS_NONFREE
#define STATUS_NONFREE 0
#endif
/**
* a struct called block_t represents the type of header of a block
* - stored in first 24 bytes
*/
typedef struct block
{
int size; // size requested by a user
int free; // free status
struct block *prev; // the previous block
struct block *next; // the next block
} block_t;
/**
* Search for the specific block header to remove in the freelist
*
* @param block_t block - the metadata header which should be removed from freelist
* @return void
*/
void remove_block_from_freelist(block_t *block);
/**
* add the specific block header into the freelist
*
* @param block_t block - the metadata header which should be added into freelist
* @return void
*/
void add_block_to_freelist(block_t *block);
/**
* search for a fixed size block in the free list
* - the searched size = sizeof(block_t) + size requested by users
*
* @param int size - the user wanted size
* @return void * - a pointer pointing to the vacant block; return NULL if not found
*/
void *request_space_from_freelist(int size);
/**
* request a fixed size block from the heap memory via mmap()
* - the requested size = sizeof(block_t) + size requested by users
* - if less than or equal to 3 pages, then a 3-page block will be allocated followed split()
* - if larger than 3 pages, then a block with exact requested size will be allocated
*
* @param int size - the user wanted size
* @return void * - a pointer pointing to the newly allocated block; return NULL if fails
*/
void *request_space_from_heap(int size);
/**
* split a large block into two blocks, one for returning back, another for remaining
* - the address of the newly allocated block would be updated into the parameter block
* - the address of the remaining block would be returned
*
* @param block_t block - the large block waiting to be splited
* @param int size - the size requested by the user
* - the requested size = sizeof(block_t) + size requested by users
* @return block_t block - the metadata block header waiting to be added into the freelist
*/
block_t *split(block_t *block, int size);
/**
* obtain the starting address of user block based on the current block_t
* - the starting address = the address of block_t block + sizeof(block)
*
* @param block_t *block - the metadata header
* @return void * - a void pointer pointing to the user block
*/
void *get_block_memory_start(block_t *block);
/**
* obtain the starting address of header block_t based on the current pointer
* - the starting address = ptr - sizeof(block)
*
* @param void *ptr - pointer
* @return void * - a void pointer pointing to the block header block_t
*/
void *get_block_header_start(void *ptr);
/**
* merge two blocks if they are continuous free blocks in the freelist
* - if starting address of block_t block + sizeof(block_t) + user size
* = starting address of the next block_t
* - then two blocks are adjacent
*
* @return void
*/
void merge_adjacent_blocks();
/**
* a debugging method to show the current freelist information
* - including information of each block in the iteration process
*
* @return void
*/
void debug_show_freelist();
/**
* a debugging method to show the curernt block information
* - including the size, free status, the previous block, the next block
*
* @return void
*/
void debug_show_block(block_t *block);
block_t *free_head = NULL; // the head of the free list
pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; // the global mutex
int freelist_size = 0;
void *myalloc(int size)
{
if (size <= 0)
{
perror("Invalid memory size request: size need to be greater than 0 \n");
return NULL;
}
void *ptr = NULL;
int final_size = size + sizeof(block_t);
pthread_mutex_lock(&global_mutex);
// request a free block from the free list
ptr = request_space_from_freelist(size);
if (!ptr)
{
// if no available block in free list, then turn to heap
ptr = request_space_from_heap(size);
if (ptr == NULL)
return NULL;
}
// set up
block_t *block = (block_t *)get_block_header_start(ptr);
block->free = STATUS_NONFREE;
debug_show_block(block);
pthread_mutex_unlock(&global_mutex);
return ptr;
}
void remove_block_from_freelist(block_t *block)
{
if (!block->prev)
{ // the block is the head
if (block->next)
free_head = block->next;
else
free_head = NULL;
}
else
block->prev->next = block->next;
if (block->next)
block->next->prev = block->prev;
}
// need to maintain a sorted freelist based on the block address
// head address should be the largest
void add_block_to_freelist(block_t *block)
{
// initialise
block->prev = NULL;
block->next = NULL;
block->free = STATUS_FREE;
// insert into a linked list sorted by address
if (!free_head || (unsigned long)free_head > (unsigned long)block)
{
if (free_head)
free_head->prev = block;
block->next = free_head;
free_head = block;
}
else
{
block_t *cur_block = free_head;
// iterate until find the correct place
while (cur_block->next && (unsigned long)cur_block->next < (unsigned long)block)
{
cur_block = cur_block->next;
}
block->next = cur_block->next;
cur_block->next = block;
}
}
block_t *split(block_t *block, int size)
{
int final_size = size + sizeof(block_t);
void *block_start = (void *)((unsigned long)block + sizeof(block_t));
// set up remaining block
block_t *remaining_block = (block_t *)((unsigned long)block_start + size);
remaining_block->size = block->size - (final_size);
// set up the block to be returned to mmap()
block->size = size;
return remaining_block;
}
void merge_adjacent_blocks()
{
block_t *cur_block = free_head;
while (cur_block->next)
{
unsigned long cur_header = (unsigned long)cur_block;
unsigned long next_header = (unsigned long)cur_block->next;
// the case where should do merging
if (next_header == cur_header + cur_block->size + sizeof(block_t))
{
cur_block->size += cur_block->next->size + sizeof(block_t);
cur_block->next = cur_block->next->next;
if (cur_block->next)
cur_block->next->prev = cur_block;
else
break;
}
else
cur_block = cur_block->next;
}
}
void myfree(void *ptr)
{
if (ptr == NULL)
return;
block_t *block = (block_t *)get_block_header_start(ptr);
if (block->free == STATUS_FREE)
{
perror("Free Failed: double free \n");
return;
}
pthread_mutex_lock(&global_mutex);
add_block_to_freelist(block);
merge_adjacent_blocks();
debug_show_freelist();
pthread_mutex_unlock(&global_mutex);
}
void *request_space_from_freelist(int size)
{
block_t *block;
int final_size = size + sizeof(block_t);
block_t *cur_block = free_head;
while (cur_block)
{
if (cur_block->size < final_size)
cur_block = cur_block->next;
else
{
remove_block_from_freelist(cur_block);
if (cur_block->size == final_size)
{
return get_block_memory_start(cur_block);
}
block_t *remaining_block = split(cur_block, size);
add_block_to_freelist(remaining_block);
return get_block_memory_start(cur_block);
}
}
return NULL;
}
void *request_space_from_heap(int size)
{
int final_size = size + sizeof(block_t);
// if requested size is larger than MIN_ALLOC_UNIT, then alloc_size should be the requested size
int alloc_size = size >= MIN_ALLOC_UNIT ? final_size : MIN_ALLOC_UNIT;
block_t *new_block = mmap(0, alloc_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (new_block == MAP_FAILED)
{
fprintf(stdout, "Allocation failded size: %ld", MIN_ALLOC_UNIT);
return NULL;
}
// set up
new_block->next = NULL;
new_block->prev = NULL;
new_block->size = alloc_size - sizeof(block_t);
if (new_block->size > final_size)
{
block_t *remaining_block = split(new_block, size);
add_block_to_freelist(remaining_block);
}
return get_block_memory_start(new_block);
}
void *get_block_memory_start(block_t *block)
{
return ((void *)((unsigned long)block + sizeof(block_t)));
}
void *get_block_header_start(void *ptr)
{
return ((void *)((unsigned long)ptr - sizeof(block_t)));
}
void debug_show_block(block_t *block)
{
fprintf(stdout, "==================================================== \nBlock Information \n");
void *ptr = get_block_memory_start(block);
fprintf(stdout, "Block header starting address: %lu \n", (unsigned long)block);
fprintf(stdout, "Block userspace starting address: %lu \n", (unsigned long)ptr);
fprintf(stdout, "Block userspace size: %d\n", block->size);
fprintf(stdout, "Block free status: %d \n", block->free);
fprintf(stdout, "==================================================== \n");
}
void debug_show_freelist()
{
fprintf(stdout, "====================================================\n");
fprintf(stdout, "Free List Debug Information \n");
block_t *cur_block = free_head;
while (cur_block)
{
debug_show_block(cur_block);
cur_block = cur_block->next;
}
fprintf(stdout, "\n");
}