Эксперимент csapp Подробное объяснение эксперимента 8-malloclab

8-Маллоклаб

Добро пожаловать на авторский блог сакура без крашеной груши одежда

Этот эксперимент следует рассматривать как самый сложный, с перекрытием различных указателей, но это также классический эксперимент, такой как бомбероб.

1 Краткий анализ

Цель эксперимента: Ознакомиться с указателями языка C, ознакомиться со способом применения динамической памяти и структурой динамической памяти.

Рабочая область: mm.c

Содержание эксперимента: напишите динамический распределитель памяти для реализации операций init, malloc, realloc, free и других, а также максимально оптимизируйте этот распределитель, чтобы сбалансировать пропускную способность и использование пространства.

Экспериментальный инструмент: инструмент mdriver, который может определять правильность

2 Конкретная реализация

1. Структура кучи

Куча — это область виртуальной памяти процесса. Первый взгляд на структуру кучи

Вставьте сюда описание изображения

Адрес кучи непрерывен, и вся область памяти является кучей.Так как же управлять всей этой областью памяти. Есть три распространенных способа

  1. Неявный свободный список:
    • Разделите всю кучу на множество соседних блоков.Заголовок каждого блока содержит информацию о размере блока и о том, выделен ли он.По размеру этого блока мы можем найти начало следующего блока, связанного с ним.адрес
    • Преимущества: Простой
    • Недостатки: каждое приложение нужно проходить с нуля, пропускная способность низкая, легко генерируется множество фрагментов пространства.
  2. Явные бесплатные списки:
    • Улучшение неявного списка свободных: на основе неявного списка свободных каждый свободный блок не только сохраняет свой размер и информацию о том, выделен ли он, но также сохраняет указатель на следующий и/или предыдущий свободный блок, так что при просмотре для свободных блоков вам нужно только пройти по списку свободных блоков
    • Преимущества: повышена эффективность распределения памяти и уменьшена фрагментация пространства.
    • Недостатки: требует много места для хранения указателей, сложно поддерживать,
  3. Отдельные бесплатные списки:
    • Поддерживайте несколько списков свободных, блоки в каждом списке примерно равны по размеру, а распределитель поддерживает массив списков свободных.
    • Преимущества: повышение эффективности распределения памяти и уменьшение фрагментации пространства.
    • Недостатки: сложно обслуживать, может привести к низкому использованию пространства.

Наша цель: сбалансировать пропускную способность и использование пространства (эти два факта фактически конфликтуют. Невозможно иметь высокую пропускную способность и высокий коэффициент использования пространства, потому что высокая пропускная способность должна использовать такие структуры, как связанные списки, хеш-таблицы, деревья, и т. д. Эти конструкции неизбежно приведут к снижению использования пространства, поэтому должен быть баланс)

2. Блочная структура

Взгляните на структуру блоков, чтобы облегчить понимание кода.

Вставьте сюда описание изображения

3. Реализация

Обратите внимание, что все этапы кода взяты из CSAPP | Lab8-Malloc Lab углубленного анализа — Zhihu (zhihu.com) , босс слишком крутой, я кратко объясню это здесь.

  1. Во-первых, определите группу макросов, с которыми легко работать. Я думаю, что эта группа макросов — гениальный ход. Роль макросов очень ясна, поэтому я не буду ее объяснять.
/* 头部/脚部大小 单字(四字节) */
#define WSIZE 4
/* 双字 */
#define DSIZE 8

/* 取最大值 */
#define MAX(a, b) ((a) > (b) ? (a) : (b))

/* 扩展堆时默认大小 */
#define CHUNKSIZE (1 << 12)

/* 设置头部/脚部值 */
#define PACK(size, alloc) ((size)|(alloc))

/* 读写指针p */
/*
	首先知道void*类型的指针可以指向任何地方,其实malloc函数申请的指针就是void*类型
	(unsigned int *)(p)的意思是将void*类型的指针p强转为指向unsigned int类型的指针
	然后(*(unsigned int *)(p))是取出该指针指向的unsigned int类型的值
*/
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) ((*(unsigned int *)(p)) = (val))

/* 从头部或脚部获取大小或分配位 */
/* 这里0x是十六进制嗷,低一位表示是否已分配 */
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)

/* 给定有效载荷指针,找到头部和脚部 */
#define HDRP(bp) ((char *)(bp) - WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)

/* 给定有效载荷指针, 找到前一块或下一块 */
#define NEXT_BLKP(bp) ((char*)(bp) + GET_SIZE(((char*)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char*)(bp) - GET_SIZE(((char*)(bp) - DSIZE)))
  1. Затем определите функции, которые вам нужно использовать. Каждая функция прокомментирована здесь.
/**
 * @brief 初始化堆
 * 初始化一个堆,主要是定义了堆的序言块和结尾块,相当于开头和结尾,然后添加了一个CHUNKSIZE/WSIZE大小的空  间
 */
int mm_init(void);

/**
 * @brief 扩展堆
 * 用于扩展堆,每当需要申请动态内存的时候就调用该函数来申请动态内存空间
 *
 * @param words	申请的动态内存大小
 */
void *extend_heap(size_t words);

/**
 * @brief 释放内存
 *
 * @param ptr	需要释放的块指针
 */
void mm_free(void *ptr);

/**
 * @brief 分割空闲块
 * 判断剩下的空间是否足够存放头部和脚部,够的话就设置各种标志位,不够的话就要设置为填充的块
 *
 * @param bp	当前空闲块指针
 * @param asize	当前请求的块大小
 */
void place(void *bp, size_t asize);

/**
 * @brief 合并块
 * 合并块有四种情况,需要把相邻的空闲块合并在一起
 *
 * @param bp	需要合并的块指针
 */
void *coalesce(void *bp);

/**
 * @brief 首次适配原则
 *
 * @param asize	请求的块大小
 */
void *first_fit(size_t asize);

/**
 * @brief 自主实现的malloc
 *
 * @param size	申请的块大小
 */
void *mm_malloc(size_t size);

/**
 * @brief 重新申请块大小
 *
 * @param ptr	重新申请的块的指针
 * @param size	重新申请的块大小
 */
void *mm_realloc(void *ptr, size_t size);

Далее идет код каждой части.Комментарии, данные шефом по каждой функции в этой части, очень подробные.Я не буду слишком уродливым, поэтому можете читать медленно.

Резюме: Наконец-то более пикантный. Хотя последний эксперимент был более водянистым, в конце концов он был завершен. Будьте полны энергии с этого момента~

рекомендация

отblog.csdn.net/m0_65591847/article/details/132922447