# [2019q3](http://wiki.csie.ncku.edu.tw/sysprog/schedule) 第 13 週測驗題
:::info
目的: 檢驗學員對 CS:APP 第 9 章的認知
:::
---
### 測驗 `1`
考慮到以下簡易的記憶體配置器實作 (`alloc.c`):
```cpp
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HEAP_INIT_SIZE 0x10000
#define HEAP_MAX_SIZE 0xF0000
#define HEAP_MIN_SIZE 0x10000
#define MIN_ALLOC_SZ 4
#define MIN_WILDERNESS 0x2000
#define MAX_WILDERNESS 0x1000000
#define BIN_COUNT 9
#define BIN_MAX_IDX (BIN_COUNT - 1)
typedef unsigned int uint;
typedef struct node_t {
uint hole, size;
struct node_t *next, *prev;
} node_t;
typedef struct {
node_t *header;
} footer_t;
typedef struct {
node_t *head;
} bin_t;
typedef struct {
long start, end;
bin_t *bins[BIN_COUNT];
} heap_t;
static uint overhead = sizeof(footer_t) + sizeof(node_t);
void add_node(bin_t *bin, node_t *node)
{
node->next = NULL;
node->prev = NULL;
node_t *temp = bin->head;
if (bin->head == NULL) {
bin->head = node;
return;
}
// we need to save next and prev while we iterate
node_t *current = bin->head;
node_t *previous = NULL;
// iterate until we get the the end of the list or we find a
// node whose size is
while (current && current->size <= node->size) {
previous = current;
current = current->next;
}
if (current == NULL) { // we reached the end of the list
previous->next = node;
node->prev = previous;
} else {
if (previous) { // middle of list, connect all links!
node->next = current;
previous->next = node;
node->prev = previous;
current->prev = node;
} else { // head is the only element
node->next = bin->head;
bin->head->prev = node;
bin->head = node;
}
}
}
void remove_node(bin_t *bin, node_t *node)
{
if (bin->head == NULL)
return;
if (bin->head == node) {
bin->head = bin->head->next;
return;
}
for (node_t *temp = bin->head->next; temp; temp = temp->next) {
if (temp == node) { // found the node
if (temp->next == NULL) { // last item
temp->prev->next = NULL;
} else { // middle item
temp->prev->next = temp->next;
temp->next->prev = temp->prev;
}
// we do not worry about deleting the head here because we already
// checked that
return;
}
}
}
node_t *get_best_fit(bin_t *bin, size_t size)
{
if (bin->head == NULL)
return NULL; // empty list!
for (node_t *temp = bin->head; temp; temp = temp->next) {
if (temp->size >= size)
return temp; // found a fit!
}
return NULL; // no fit!
}
node_t *get_last_node(bin_t *bin)
{
node_t *temp = bin->head;
while (temp->next)
temp = temp->next;
return temp;
}
uint get_bin_index(size_t sz)
{
uint index = 0;
sz = sz < 4 ? 4 : sz;
while (sz >>= 1)
index++;
index -= 2;
if (index > BIN_MAX_IDX)
index = BIN_MAX_IDX;
return index;
}
static footer_t *get_foot(node_t *node)
{
return (footer_t *) ((char *) node + sizeof(node_t) + AAA);
}
static void create_foot(node_t *head)
{
footer_t *foot = get_foot(head);
foot->header = head;
}
node_t *get_wilderness(heap_t *heap)
{
footer_t *wild_foot = (footer_t *) ((char *) heap->end - sizeof(footer_t));
return wild_foot->header;
}
void init_heap(heap_t *heap, long start)
{
node_t *init_region = (node_t *) start;
init_region->hole = BBB;
init_region->size = (HEAP_INIT_SIZE) - sizeof(node_t) - sizeof(footer_t);
create_foot(init_region);
add_node(heap->bins[get_bin_index(init_region->size)], init_region);
heap->start = (void *) start;
heap->end = (void *) (start + HEAP_INIT_SIZE);
}
void *heap_alloc(heap_t *heap, size_t size)
{
uint index = get_bin_index(size);
bin_t *temp = (bin_t *) heap->bins[index];
node_t *found = get_best_fit(temp, size);
while (found == NULL) {
if (index + 1 >= BIN_COUNT)
return NULL;
temp = heap->bins[++index];
found = get_best_fit(temp, size);
}
if ((found->size - size) > (overhead + MIN_ALLOC_SZ)) {
node_t *split =
(node_t *) (((char *) found + sizeof(node_t) + sizeof(footer_t)) +
size);
split->size = found->size - size - sizeof(node_t) - sizeof(footer_t);
split->hole = 1;
create_foot(split);
uint new_idx = get_bin_index(split->size);
add_node(heap->bins[new_idx], split);
found->size = size;
create_foot(found);
}
found->hole = 0;
remove_node(heap->bins[index], found);
node_t *wild = get_wilderness(heap);
if (wild->size < MIN_WILDERNESS)
return NULL;
found->prev = NULL;
found->next = NULL;
return &found->next;
}
static int offset = 8;
void heap_free(heap_t *heap, void *p)
{
footer_t *new_foot, *old_foot;
node_t *head = (node_t *) ((char *) p - offset);
if (head == (node_t *) (uintptr_t) heap->start) {
head->hole = CCC;
add_node(heap->bins[get_bin_index(head->size)], head);
return;
}
node_t *next = (node_t *) ((char *) get_foot(head) + sizeof(footer_t));
footer_t *f = (footer_t *) ((char *) head - DDD);
node_t *prev = f->header;
if (prev->hole) {
bin_t *list = heap->bins[get_bin_index(prev->size)];
remove_node(list, prev);
prev->size += overhead + head->size;
new_foot = get_foot(head);
new_foot->header = prev;
head = prev;
}
if (next->hole) {
bin_t *list = heap->bins[get_bin_index(next->size)];
remove_node(list, next);
head->size += overhead + next->size;
old_foot = get_foot(next);
old_foot->header = 0;
next->size = 0;
next->hole = 0;
new_foot = get_foot(head);
new_foot->header = head;
}
head->hole = 1;
add_node(heap->bins[get_bin_index(head->size)], head);
}
int main(int argc, char **argv)
{
heap_t *heap = malloc(sizeof(heap_t));
memset(heap, 0, sizeof(heap_t));
void *region = malloc(HEAP_INIT_SIZE);
memset(region, 0, HEAP_INIT_SIZE);
for (int i = 0; i < BIN_COUNT; i++) {
heap->bins[i] = malloc(sizeof(bin_t));
memset(heap->bins[i], 0, sizeof(bin_t));
}
init_heap(heap, (long) region);
printf("overhead = %d \n", overhead);
void *a = heap_alloc(heap, 8);
printf("a = %d size: 8 \n", (int) a);
void *b = heap_alloc(heap, 128);
printf("b = %d size: 128 \n", (int) b);
void *c = heap_alloc(heap, 8);
printf("c = %d size: 8 \n", (int) c);
printf("\nfreeing b \n");
heap_free(heap, b);
void *d = heap_alloc(heap, 8);
printf("d = %d size: 8 \n", (int) d);
void *e = heap_alloc(heap, 16);
printf("e = %d size: 16 \n", (int) e);
void *f = heap_alloc(heap, 8);
printf("f = %d size: 8 \n", (int) f);
void *g = heap_alloc(heap, 8);
printf("g = %d size: 8 \n", (int) g);
printf("\nfreeing d & f \n");
heap_free(heap, d);
heap_free(heap, f);
printf("\nfreeing e\n");
heap_free(heap, e);
void *h = heap_alloc(heap, 128);
printf("h = %d size: 128 \n", (int) h);
printf("\n");
for (int i = 0; i < BIN_COUNT; i++) {
free(heap->bins[i]);
}
free(heap);
free(region);
}
```
在 GNU/Linux 上參考的執行輸出如下:
```shell
overhead = 32
a = -1337560376 size: 8
b = -1337560336 size: 128
c = -1337560176 size: 8
freeing b
d = -1337560336 size: 8
e = -1337560296 size: 16
f = -1337560248 size: 8
g = -1337560136 size: 8
freeing d & f
freeing e
h = -1337560336 size: 128
```
請揣摩程式碼及其註解,補完程式碼。
==作答區==
AAA = ?
`(a)` 0
`(b)` `sizeof(footer_t)`
`(c)` 1
`(d)` `node->hole`
`(e)` `node->size`
BBB = ?
`(a)` 0
`(b)` `sizeof(footer_t)`
`(c)` 1
`(d)` `node->hole`
`(e)` `node->size`
CCC = ?
`(a)` 0
`(b)` `sizeof(footer_t)`
`(c)` 1
`(d)` `node->hole`
`(e)` `node->size`
DDD = ?
`(a)` 0
`(b)` `sizeof(footer_t)`
`(c)` 1
`(d)` `node->hole`
`(e)` `node->size`
:::success
延伸問題:
1. 解釋上述程式碼運作機制;
2. 指出實作的缺失並加以改進;
3. 參考 [Two Level Segregated Fit (TLSF) memory allocator implementation](https://github.com/jserv/tlsf-bsd) 實作和對應的論文,分析運作原理;
:::
---