csapp-Experiment 8-malloclab-Experiment ausführliche Erklärung

8-Malloclab

Willkommen im Blog des Autors, Sakura, keine bemalte Birnenkleidung

Dieses Experiment sollte als das schwierigste angesehen werden, da sich verschiedene Hinweise überschneiden, aber es ist auch ein klassisches Experiment wie Bomblab.

1 Kurze Analyse

Zweck des Experiments: Machen Sie sich mit C-Sprachzeigern, der Anwendungsmethode des dynamischen Speichers und der Struktur des dynamischen Speichers vertraut

Arbeitsbereich: mm.c

Experimentinhalt: Schreiben Sie einen dynamischen Speicherzuordner, um init-, malloc-, realloc-, free- und andere Vorgänge zu implementieren, und optimieren Sie diesen Zuordner so weit wie möglich, um Durchsatz und Speicherplatznutzung auszugleichen

Experimentelles Tool: Mdriver-Tool, das die Richtigkeit erkennen kann

2 Konkrete Umsetzung

1. Heap-Struktur

Der Heap ist der virtuelle Speicherbereich eines Prozesses. Schauen Sie sich zunächst die Heap-Struktur an

Fügen Sie hier eine Bildbeschreibung ein

Die Adresse des Heaps ist kontinuierlich und der gesamte Speicherbereich ist der Heap. Wie verwaltet man also diesen gesamten Speicherbereich? Es gibt drei gängige Methoden

  1. Implizite freie Liste:
    • Teilen Sie den gesamten Heap in viele benachbarte Blöcke auf. Der Header jedes Blocks enthält Informationen über die Größe des Blocks und ob er zugewiesen ist. Anhand der Größe dieses Blocks können wir den Anfang des nächsten damit verbundenen Blocks finden. Adresse
    • Vorteile: Einfach
    • Nachteile: Jede Anwendung muss von Grund auf durchlaufen werden, der Durchsatz ist gering und es werden leicht viele Raumfragmente generiert.
  2. Explizite kostenlose Listen:
    • Eine Verbesserung gegenüber der impliziten freien Liste. Basierend auf der impliziten freien Liste speichert jeder freie Block nicht nur seine Größe und Informationen darüber, ob er zugewiesen ist, sondern auch einen Zeiger auf den nächsten und/oder vorherigen freien Block, sodass beim Suchen Für freie Blöcke müssen Sie nur die freie Liste durchlaufen
    • Vorteile: Verbesserte Speicherzuteilungseffizienz und geringere Speicherplatzfragmentierung
    • Nachteile: Benötigt viel Platz zum Speichern von Zeigern, schwer zu warten,
  3. Getrennte kostenlose Listen:
    • Verwalten Sie mehrere freie Listen. Die Blöcke in jeder Liste sind ungefähr gleich groß und der Allokator verwaltet ein Array freier Listen
    • Vorteile: Verbessern Sie die Effizienz der Speicherzuweisung und reduzieren Sie die Speicherplatzfragmentierung
    • Nachteile: Schwierig zu warten, kann zu geringer Platzausnutzung führen

Unser Ziel: die Durchsatzrate und die Raumnutzung auszugleichen (diese beiden stehen tatsächlich im Konflikt). Es ist unmöglich, eine hohe Durchsatzrate und eine hohe Raumnutzungsrate zu haben, da eine hohe Durchsatzrate Strukturen wie verknüpfte Listen, Hash-Tabellen, Bäume usw. verwenden muss. usw. Diese Strukturen führen unweigerlich zu einer geringeren Raumausnutzung, daher muss ein Ausgleich geschaffen werden)

2. Blockstruktur

Schauen Sie sich die Blockstruktur an, um das Verständnis des Codes zu erleichtern

Fügen Sie hier eine Bildbeschreibung ein

3. Umsetzung

Beachten Sie, dass die Codestufen alle von CSAPP | Lab8-Malloc Lab stammen, eingehende Analyse - Zhihu (zhihu.com) , der Chef ist zu großartig, ich werde es hier kurz erklären.

  1. Definieren Sie zunächst eine Reihe von Makros, die einfach zu bedienen sind. Ich halte diese Reihe von Makros für einen Geniestreich. Die Rolle von Makros ist sehr klar, daher werde ich sie nicht erklären.
/* 头部/脚部大小 单字(四字节) */
#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. Definieren Sie dann die Funktionen, die Sie verwenden müssen. Jede Funktion wird hier kommentiert.
/**
 * @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);

Als nächstes folgt der Code für jeden Teil. Die Kommentare des Chefs zu jeder Funktion in diesem Teil sind sehr detailliert. Ich werde nicht zu hässlich sein, also können Sie es langsam lesen.

Zusammenfassung: Endlich würziger. Obwohl das letzte Experiment wässriger war, wurde es endlich beendet. Seien Sie von nun an voller Energie~

Ich denke du magst

Origin blog.csdn.net/m0_65591847/article/details/132922447
Empfohlen
Rangfolge