当前位置: 首页 > news >正文

计算机系统基础实训七-MallocLab实验

实验目的与要求

1、让学生理解动态内存分配的工作原理;

2、让学生应用指针、系统级编程的相关知识;

3、让学生应用各种动态内存分配器的实现方法;

实验原理与内容

(1)动态内存分配器基本原理

动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合来维护,每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。

分配器有两种基本风格:显式分配器和隐式分配器。两种风格都要求应用显式地分配块。它们的不同之处在于由哪个实体来负责释放已分配的块。

显式动态内存分配器要求应用显式地释放任何已分配的块。例如C程序通过调用malloc函数来分配一个块,通过调用free函数来释放一个块。其中malloc采用的总体策略是:先系统调用sbrk一次,会得到一段较大的并且是连续的空间。进程把系统内核分配给自己的这段空间留着慢慢用。之后调用malloc时就从这段空间中分配,free回收时就再还回来(而不是还给系统内核)。只有当这段空间全部被分配掉时还不够用时,才再次系统调用sbrk。当然,这一次调用sbrk后内核分配给进程的空间和刚才的那块空间一般不会是相邻的。

隐式动态内存分配器也叫做垃圾收集器,例如,诸如Lisp、ML、以及Java之类的高级语言就依赖垃圾收集来释放已分配的块。

(2)隐式空闲链表

对于带边界标记的隐式空闲链表分配器,一个块是由一个字的头部、有效载荷、可能的一些额外的填充,以及在块的结尾处的一个字的脚部组成的。头部编码了这个块的大小(包括头部和所有的填充),以及这个块是已分配的还是空闲的。如果我们强加一个双字的对齐约束条件,那么块大小就总是8的倍数,且块大小的最低3位总是0。因此,我们只需要内存大小的29个高位,释放剩余的3位来编码其他信息。在这种情况中,我们用其中的最低位(已分配位)来指明这个块是已分配的还是空闲的。头部后面就是应用调用malloc时请求的有效载荷。有效载荷后面是一片不使用的填充块,其大小可以是任意的。需要填充有很多原因。比如,填充可能是分配器策略的一部分,用来对付外部碎片。或者也需要用它来满足对齐要求。  

我们将对组织为一个连续的已分配块和空闲块的序列,这种结构称为隐式空闲链表,是因为空闲块是通过头部中的大小字段隐含地连接着的。分配器可以通过遍历堆中所有的块,从而间接地遍历整个空闲块的集合。注意:此时我们需要某种特殊标记的结束块,可以是一个设置了已分配位而大小为零的终止头部。  

Knuth提出了一种边界标记技术,允许在常数时间内进行对前面块的合并。这种思想是在每个块的结尾处添加一个脚部,其中脚部就是头部的一个副本。如果每个块包括这样一个脚部,那么分配器就可以通过检查它的脚部,判断前面一个块的起始位置和状态,这个脚部总是在距当前块开始位置一个字的距离。

(3)显式空闲链表

根据定义,程序不需要一个空闲块的主体,所以实现空闲链表数据结构的指针可以存放在这些空闲块的主体里面。显式空闲链表结构将堆组织成一个双向空闲链表,在每个空闲块的主体中,都包含一个pred(前驱)和succ(后继)指针。使用双向链表而不是隐式空闲链表,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。不过,释放一个块的时间可以是线性的,也可能是个常数,这取决于空闲链表中块的排序策略。一种方法是用后进先出(LIFO)的顺序维护链表,将新释放的块放置在链表的开始处。另一种方法是按照地址顺序来维护链表,其中链表中每个块的地址都小于它后继的地址。

在本实验中,您将使用C语言编写一个显式动态内存分配器,我们鼓励您创造性地进行设计探索,以实现正确、高效和快速的分配器。

实验设备与软件环境

1.Linux操作系统—64位 Ubuntu 18.04

2. C编译环境(gcc)

3. 计算机

实验过程与结果(可贴图)

1、初始化内存管理系统的函数mm_init

通过mem_sbrk系统调用来分配初始的内存堆大小HEAP_INITSIZE。如果分配失败,函数返回-1。成功后,它初始化管理内存的各个数据结构:设置空闲块树的根节点为NULL,表示初始时树为空;内存块链表也初始化为空;通过设置边界标签,确保内存分配不会跨越预设的堆边界;初始化块分割策略的奇偶标志;最后,将除去边界标签占用空间之外的整个初始内存区域作为一个大的空闲块加入到空闲块管理队列中。如果一切顺利,函数返回0,表示初始化成功。

 

2、初始化堆mm_free,作用是释放指向动态分配内存的指针所指向的内存空间。函数首先获取要释放块的大小,并准备合并操作。接着,检查当前块前后是否有已释放的空间:如果后方有空闲块,则将其合并至当前块并调整总大小;如果前方有空闲块,则更新起始位置、合并大小,并同样向前合并。完成相邻空闲块的合并后,更新后的大型空闲内存块会被重新登记到内存管理器的空闲列表中,等待未来的分配请求。这一过程减少了内存碎片,提高了内存分配的效率。

2、内存重分配函数*mm_realloc:负责调整给定指针ptr指向的内存块的大小到size指定的大小,同时尽量保持原有数据的完整性。

① 如果输入的指针ptr为空,函数相当于执行一次新的内存分配,调用mm_malloc(size)来分配指定大小的内存,并返回新分配的内存地址。

② 如果请求的大小size为0,意味着希望释放内存而非重新分配,因此直接调用mm_free(ptr)释放内存并返回NULL。

③ 检查当前块的大小是否已经大于等于请求的大小加上8字节(可能是为了对齐或其他内部管理需要)。如果是,直接返回原指针,无需重新分配。

④ 如果下一个内存块是空闲的,并且合并后的大小能满足新的需求,函数会合并这两个块,更新大小标签,并返回原指针。

⑤ 如果下一个块是空闲的且紧邻内存区域边界,函数尝试扩展内存堆,通过调用mem_sbrk增加内存,并更新大小标签。

⑥ 如果当前块已经是最后一个块,无法直接扩展,函数会尝试增加内存堆的大小以满足新尺寸要求,然后调整当前块的大小并返回原指针。

⑦ 如果以上条件都不满足,即不能在当前位置调整大小或扩展,函数会在内存中另找一个合适的位置(通过调用mm_malloc(size)),复制原数据到新位置,释放旧内存,然后返回新内存地址。

3、自定义的内存分配函数mm_malloc:用于从内存堆中分配指定大小的内存空间。其工作流程涉及查找合适的空闲块、内存堆扩展、块分割及内存管理等。

函数首先根据请求的大小调整并确保内存对齐,然后尝试从现有的空闲块中分配。如果找不到合适的空闲块,则通过mem_sbrk系统调用扩大内存堆。在分配到足够大的块后,若块的剩余部分大于一定阈值,则会分割该块并将剩余部分重新加入空闲块管理结构中,以优化内存使用。最后,函数会设置新分配块的标签,表示该内存已被分配,并在调试模式下打印相关信息。

实验总结

在本次计算机系统基础实验中,我深入了解并实践了动态内存分配器的工作原理,特别是显式分配器的设计与实现。通过C语言,我在Ubuntu系统上编写了具备显式内存分配器,实现了内存的高效管理,从初始化、释放、重分配、调整大小直至优化分配一整套流程,解决了内存碎片化、资源不足、泄露、分配效率低效等问题,提升整体利用,实践展现了内存对指针、系统编程、内存管理的综合应用能力,实验的成果不仅在于实现了分配器本身,更在于对内存管理的深入理解与解决复杂问题的能力提升。

/**  malloc.c:  A Reasonably Efficient Best-Fit Allocator.*/#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>#include "memlib.h"
#include "mm.h"/*  Team Information  */
team_t team = {"Tree-based free list (CMU CS 213 students,  Fall 2000)","Vinny Furia","vmf@andrew.cmu.edu","Nick Carter","nbc@andrew.cmu.edu"
};//int split_parity = 1;/*  Compilation Options - to be removed eventually   */
#define _DEBUG_          0     /* Prints out lots of crap *//*  Typedefs for Heap Data Structures  */
typedef struct List {void** BLAH;struct List* Prev;struct List* Next;void** BLAH2;
} List;typedef struct Node {struct Node* Left;struct Node* Right;int Color;struct Node* Parent;
} Node;/*  Named Constants  */
#define HEAP_INITSIZE    8232   /* multiple of 8 */
#define HEAP_GROWSIZE    4096   /* multiple of 8 */
#define REALLOC_GROWSIZE   2048  /* multiple of 8 */
//#define REALLOC_BUBBLE   128     /* blocksize     */
//#define REALLOC_BUBBLEINCREMENT 128
#define RED              1
#define BLACK            0
#define FALSE            0/*  Masks for Boundary Tags  */
#define SIZE_MASK        0xFFFFFFF8
#define TREE_MASK        4
#define BLOB_MASK        2
#define FREE_MASK        1
#define IN_TREE          (FREE_MASK|TREE_MASK)
#define IN_BLOB          (FREE_MASK|BLOB_MASK)
#define ALLOCATED        0/*  Named Heap Locations  */
#define treeroot         ((Node**)(char *)mem_heap_lo())
#define blobroot         ((List**)(char *)mem_heap_lo()+1)
#define boundtag_lo      (((int*)((char *)mem_heap_lo()+16)))
#define boundtag_hi      (((int*)((char *)mem_heap_hi()-3 ))) 
#define split_parity     (*((int*)(char *)mem_heap_lo()+12))/*  Macros for Boundary Tags  *///  The following 2 are just for use in definitions
#define __LowTag(p)      (*((int*)(p)-1))
#define __HiPrevTag(p)   (*((int*)(p)-2))//  The next few can be safely used with all block pointers
#define Size(p)          (__LowTag(p) & SIZE_MASK)
#define IsFree(p)        (__LowTag(p) & FREE_MASK)
#define IsInBlob(p)      (__LowTag(p) & BLOB_MASK)
#define IsInTree(p)      (__LowTag(p) & TREE_MASK)
#define NextBlock(p)     ((char *)(p) + Size(p))
#define PrevFree(p)      (__HiPrevTag(p) & FREE_MASK)
#define NextFree(p)      (IsFree(NextBlock(p)))//  PrevBlock(p) is undefined for the first addressable block.
#define PrevBlock(p)     ((char *)(p) - (__HiPrevTag(p) & SIZE_MASK))/*  Tree Node Macros  */
#define IsRed(p)        ((p!=NULL) && (((Node*)p)->Color == RED))/** Invariants:*  - The word starting at (mem_heap_lo()) is a pointer to the root*    node of the freetree.*    If this value is null, then there are no free blocks.*  - The word starting at (mem_heap_lo()+8) is a fake upper boundary*    tag, with size 0, and flagged as allocated.*  - The word starting at (deseg_hi-3) is a fake lower boundary*    tag, with size 0, and flagged as allocated.*  - Every block has a "front" and "rear" tag.  This tag is 4 bytes*    (1 word) being an integer of the size of the block.*    The semantics of these tags are as follows:*      - By masking off the least significant 3 bits, we get*        the size of the block in bytes.*      - Combinations of the last 3 bits are interpreted as follows:*          0 0 0  =>  Block is allocated*          1 1 1  =>  Block is a red node in the free tree.*          1 0 1  =>  Block is a black node in the free tree.*          0 0 1  =>  Block is on the trailing list of some*                     node in the free tree.*          0 1 1  =>  Block is free but not in the tree; instead*                     it is in the doubly-linked list of recently*                     freed nodes (the blob).*  - Free blocks have front and rear tags as described above, and*    the front tag is followed by three pointers.  These pointers are*    (in order):  Left Child, Right Child, List Next.  Each of these*    is one word in length (4 bytes).  All Free blocks will be*    maintained within a Red-Black tree with blocks of the same*    size being in an address ordered linked list.*  - Allocated Blocks will have front and rear tags as described*    previously with block data between these tags.**  - At a** Policies:**  - Free blocks are coalesced immediately and added to the freetree.*  - Allocation is done best-fit.  When there are several candidates for*    the best-fit, then the lowest-addressed block is chosen.** Conventions:**  - Block pointers are treated as (int*)'s*/void setTags (void* block, size_t size, int flags)
{int* tag1 = (int*)block - 1;int* tag2 = (int*)((char *)block+size)-2;*tag1 = *tag2 = (size | flags);
}/** void left_rotate(Node* x)**       x        -->        y*      / \                 / \*     T1  y      -->      x  T3*        / \             / \*       T2 T3    -->    T1 T2** Precondition: x must be non-null with a non-null right child.*/
void left_rotate (Node* x) {Node* y = x->Right;x->Right = y->Left;if ( y->Left != NULL )y->Left->Parent = x;//Set the parent to point to y instead of xy->Parent = x->Parent;if ( x->Parent == NULL )*treeroot = y;elseif ( x == x->Parent->Left )//x was on the left of its parentx->Parent->Left = y;else//x must have been on the rightx->Parent->Right = y;y->Left = x;x->Parent = y;
}/** void right_rotate(Node* x)**           x      -->       y*          / \              / \*         y  T3    -->     T1  x*        / \                  / \   *       T1 T2      -->       T2 T3** Precondition: x must be non-null with a non-null left child.*/
void right_rotate (Node* x) {Node* y = x->Left;x->Left = y->Right;if (y->Right != NULL)y->Right->Parent = x;y->Parent = x->Parent;// Set the parent to point to y instead of xif (x->Parent == NULL) *treeroot = y;elseif (x == x->Parent->Left)// x was on the left of its parentx->Parent->Left = y;else// x must have been on the rightx->Parent->Right = y;y->Right = x;x->Parent = y;
}/*  Color Functions  */
int isRed (Node* x) {return (x != NULL) && (x->Color == RED);
}int isBlack (Node* x) {return (x == NULL) || (x->Color == BLACK);
}void setblack (Node* x) {if (x!=NULL)x->Color = BLACK;
}void setred (Node* x) {// NULL nodes are never red.x->Color = RED;
}/** int isLess(Node* x, Node* y)** This expression defines the order by which* the tree is constructed.  x and y may not be NULL.**/
int isLess (Node* x, Node *y) {return (Size(x) < Size(y)) || ((Size(x) == Size(y)) && x<y);
}/** int tree_insert(Node* x)** Preconditions:*     - x should be an established node*       (i.e. size flags, IN_TREE set) that*       does not appear already in the tree.** Postconditions:*     Inserts the node into the tree as follows:*     - If the tree is currently empty, then we set x*       to be the root, color it black, and return 0.*     - Otherwise, we insert x as a leaf, color it red,*       and return 1.**/
int tree_insert (Node* x) {Node* current = *treeroot;//Empty tree --> update root pointerif (current == NULL) {*treeroot = x;x->Parent = NULL;x->Right = x->Left = NULL;x->Color = BLACK;return 0;}//Non-empty tree --> insert as leafwhile(1) {if (isLess(x,current)) {//x belongs in the left child of current nodeif (current->Left != NULL)current = current->Left;else {current->Left = x;x->Parent = current;x->Right = x->Left = NULL;x->Color = RED;return 1; }}else {//x belongs in the right child of current nodeif (current->Right != NULL)current = current->Right;else {current->Right = x;x->Parent = current;x->Right = x->Left = NULL;x->Color = RED;return 1;}}}
}void freetree_insert (void* ptr, size_t size) {Node* x;Node* y;//Write the tags for the blocksetTags(ptr, size, IN_TREE);x = (Node*)ptr;//Do a tree insertionif (tree_insert(x) == 0){// No further work needed.return;}/* Move up the tree to restore the red/black property.       *//* Invariant: x is red, and red/black properties are every-  *//*            where satisfied, except maybe between x and    *///            x->Parent.                                     */while ( (x->Parent != NULL)&& (isRed(x->Parent))&& (x->Parent->Parent != NULL)) {if ( x->Parent == x->Parent->Parent->Left ) {/* If x's parent is a left, y is x's right 'uncle' */y = x->Parent->Parent->Right;if ( isRed(y) ) {/* case 1 - change the colours */setblack(x->Parent);  setblack(y);setred(x->Parent->Parent);/* Move x up the tree */x = x->Parent->Parent;}else {/* y is a black node */if ( x == x->Parent->Right ) {/* and x is to the right */ /* double-rotate . . .  */left_rotate( x->Parent );right_rotate( x->Parent );setblack( x->Left );}else{/* single-rotate */setblack(x);x = x->Parent;right_rotate( x->Parent );}}}else {/* If x's parent is a right, y is x's left 'uncle' */y = x->Parent->Parent->Left;if ( isRed(y) ) {/* case 1 - change the colours */setblack(x->Parent);setblack(y);setred(x->Parent->Parent);/* Move x up the tree */x = x->Parent->Parent;}else {/* y is a black node */if ( x == x->Parent->Left ) {/* and x is to the left *//* double rotate */right_rotate( x->Parent );left_rotate( x->Parent );setblack(x->Right);}else {/* single rotate */setblack(x);x = x->Parent;left_rotate( x->Parent );}}}}/* Colour the root black */setblack(*treeroot);
}Node* freetree_locate(int size) {Node* best = NULL;Node* current = *treeroot;//Find the smallest (with respect to tree-order) element//for which size <= Size(current), assuming that size-//insufficiency is preserved by the tree-order.while(current != NULL) {if (size <= Size(current)) {best = current;current = current->Left;}elsecurrent = current->Right;}return best;
}int freetree_locatemax()
{Node* n = *treeroot;if (n == NULL)return 0;else{while (n->Right)n = n->Right;}return Size(n);
}void left_child_is2x(Node* x);
void right_child_is2x(Node* x);//left child is a double-black node.  Fix it.
void left_child_is2x(Node* x){Node* sis = x->Right;if (sis->Color == RED){left_rotate(x);x->Color = !(x->Color);sis->Color = !(sis->Color);sis = x->Right;}//Now sis is black.  Let's check its children.if (isBlack(sis->Right) && isBlack(sis->Left)){sis->Color = RED;if (x->Color == RED){x->Color = BLACK;return;  //done!}else{//move violation up to parent, if any.//if node is root, it's already black, and we're done.if (x->Parent != NULL){if (x->Parent->Left == x)left_child_is2x(x->Parent);elseright_child_is2x(x->Parent);}return;}}if (isBlack(sis->Right))  //farther child is black{//make it so that the farther child is redright_rotate(sis);sis->Color = RED;   //used to be black, old sissis = x->Right;sis->Color = BLACK;  //used to be red.  New sis}//now we know that sis->Right is red. This is fixable.left_rotate(x);sis->Color = x->Color;      //just to copy.x->Color = BLACK;           //was indeterminate.sis->Right->Color = BLACK;  //was red.return;
}void right_child_is2x(Node* x){Node* sis = x->Left;if (sis->Color == RED){right_rotate(x);x->Color = !(x->Color);sis->Color = !(sis->Color);sis = x->Left;}//Now sis is black.  Let's check its children.if (isBlack(sis->Right) && isBlack(sis->Left)){sis->Color = RED;if (x->Color == RED){x->Color = BLACK;return;  //done!}else{//move violation up to parent, if any.//if node is root, it's already black, and we're done.if (x->Parent != NULL){if (x->Parent->Left == x)left_child_is2x(x->Parent);elseright_child_is2x(x->Parent);}return;}}if (isBlack(sis->Left))  //farther child is black{//make it so that the farther child is redleft_rotate(sis);sis->Color = RED;   //used to be black, old sissis = x->Left;sis->Color = BLACK;  //used to be red.  New sis}//now we know that sis->Left is red. This is fixable.right_rotate(x);sis->Color = x->Color;      //just to copy.x->Color = BLACK;           //was indeterminate.sis->Left->Color = BLACK;   //was red.return;
}void freetree_delete( Node* z ) {/******************************  delete node z from tree  ******************************/if (z == NULL)return;if ((z->Left == NULL || z->Right == NULL) && z->Color == RED){Node* child = z->Left ? z->Left : z->Right;  //is blackif (child)child->Parent = z->Parent;if (z->Parent == NULL)*treeroot = child;else if (z->Parent->Left == z)z->Parent->Left = child;elsez->Parent->Right = child;return;}else if ((z->Left == NULL || z->Right == NULL) && z->Color == BLACK){Node* child = z->Left ? z->Left : z->Right;if (child)child->Parent = z->Parent;if (z->Parent == NULL){*treeroot = child;setblack(child);return;}else if (z->Parent->Left == z){z->Parent->Left = child;left_child_is2x(z->Parent);return;}else{z->Parent->Right = child;right_child_is2x(z->Parent);return;}}else if (z->Right->Left == NULL && z->Right->Color == RED){//We know that z->Left is non-nullz->Right->Left = z->Left;z->Left->Parent = z->Right;z->Right->Parent = z->Parent;z->Right->Color = BLACK;if (z->Parent == NULL)*treeroot = z->Right;else if (z->Parent->Left == z)z->Parent->Left = z->Right;elsez->Parent->Right = z->Right;return;}else if (z->Right->Left == NULL && z->Right->Color == BLACK){z->Right->Left = z->Left;z->Left->Parent = z->Right;z->Right->Parent = z->Parent;z->Right->Color = z->Color;if (z->Parent == NULL)*treeroot = z->Right;else if (z->Parent->Left == z)z->Parent->Left = z->Right;elsez->Parent->Right = z->Right;right_child_is2x(z->Right);return;}else{Node* y = z->Right->Left;Node  y2;while (y->Left)y = y->Left;y2 = *y;*y = *z;if (z->Parent == NULL)*treeroot = y;else if (z->Parent->Left == z)z->Parent->Left = y;elsez->Parent->Right = y;y->Left->Parent = y;y->Right->Parent = y;//now y has replaced z.  Clean up y2, where y used to be.y2.Parent->Left = y2.Right;if (y2.Right)y2.Right->Parent = y2.Parent;if (y2.Color == RED)return;else{left_child_is2x(y2.Parent);return;}}
}/** Invariant: ptr points to a block that is free.* Afterwards, the pointer will be deleted from* the relevant data structures, but its tags* will not reflect the change.*/
void delFromWherever (void *ptr)
{if (IsInTree(ptr))freetree_delete(ptr);else if (IsInBlob(ptr)){//The node is in the blob.  Remove it in O(1) time.List* L = ptr;if (L->Next != NULL)L->Next->Prev = L->Prev;if (L->Prev)L->Prev->Next = L->Next;else*blobroot = L->Next;}
}//Takes a block, sets its tags, and puts it in the blob
void queueNewFreeBlock(void* ptr, int size)
{//Mark and insert into the blob.List* L = ptr;setTags(ptr, size, IN_BLOB);L->Prev = NULL;L->Next = *blobroot;if (L->Next)L->Next->Prev = L;*blobroot = L;
}//takes all items from the blob and inserts into the freetree
void emptyblob()
{/*  Move all blob-blocks into the tree  */List* N = *blobroot;while (N!=NULL){List* temp = N->Next;freetree_insert(N,Size(N));N = temp;}*blobroot = NULL;
}int mm_init(void)
{// 获取初始所需的内存空间if (mem_sbrk(HEAP_INITSIZE) == (void *)-1)return -1; // 如果申请失败,返回-1// 初始化时,空闲块树中有一个自由块*treeroot = NULL; // 内存块链表(Blob)最初为空*blobroot = NULL;// 设置边界标签,防止内存块与堆边界合并*boundtag_lo = 0;*boundtag_hi = 0;// 初始化块分割的奇偶标志split_parity = 0;// 将除边界标签外的初始内存区域加入到空闲块队列中,作为一大块可用内存queueNewFreeBlock(boundtag_lo + 2, HEAP_INITSIZE - 24);return 0; // 初始化成功,返回0
}void mm_free (void *ptr)
{//内存块的合并int new_size     = Size(ptr);void *new_block  = ptr;void *prev_block;void *next_block;if (NextFree(ptr)){next_block = NextBlock(ptr);new_size += Size(next_block);delFromWherever(next_block);}if (PrevFree(ptr)){prev_block = PrevBlock(ptr);new_size += Size(prev_block);new_block = prev_block;delFromWherever(prev_block);}//加入到空闲内存块的管理队列queueNewFreeBlock(new_block,new_size);
}void *mm_realloc(void *ptr, size_t size)
{void* next_block = NextBlock(ptr); // 获取当前块的下一个内存块void* ptr2;// 如果ptr为NULL,表现如malloc,分配指定大小的内存if (ptr == NULL) {return mm_malloc(size);}// 如果请求的大小为0,释放原内存并返回NULLif (size == 0) {mm_free(ptr);return NULL;}// 如果当前块大小足够,直接返回原指针if (Size(ptr) >= (size + 8)) {return ptr;}// 检查是否能通过合并下一个空闲块满足新大小需求if (IsFree(next_block) && (Size(next_block) + Size(ptr) >= size + 8)) {size = Size(next_block) + Size(ptr); // 合并两个块的大小delFromWherever(next_block); // 从管理结构中移除下一个块setTags(ptr, size, 0); // 更新当前块的大小和状态标签return ptr;}// 尝试扩展到下一个块之后,如果它是最后一个块if (IsFree(next_block) && (NextBlock(next_block) > (char *)boundtag_hi)) {delFromWherever(next_block); // 移除下一个空闲块的记录setTags(ptr, Size(ptr) + Size(next_block), 0); // 扩展当前块的大小next_block = NextBlock(next_block); // 移动到原本的下一个块之后的位置}// 如果当前块已是最后一块if (next_block > (void *)boundtag_hi) {// 计算最小扩展大小并留出额外增长空间int min_size = ((size + 15) & -8) + 8;int grow_size = min_size - Size(ptr) + REALLOC_GROWSIZE;grow_size &= REALLOC_GROWSIZE; // 确保增长量符合特定对齐或策略// 尝试扩展内存堆if (mem_sbrk(grow_size) == (void *)-1) {// 扩展失败,返回NULLreturn NULL;}*boundtag_hi = 0; // 更新边界标记setTags(ptr, Size(ptr) + grow_size, ALLOCATED); // 更新块的大小和状态return ptr;}// 如果上述条件都不满足,需要分配新块并复制数据ptr2 = mm_malloc(size);memcpy(ptr2, ptr, Size(ptr) - 8); // 复制数据(注意:实际拷贝大小可能需调整)mm_free(ptr); // 释放原内存#if _DEBUG_// 调试输出printf("REALLOC: 原内存位于%p,新内存位于%p...\n", ptr, ptr2);printAllBlocks(); // 打印所有内存块信息#endif //_DEBUG_return ptr2; // 返回新分配的内存地址
}void *mm_malloc (size_t size)
{void* block; // 指向分配的内存块int block_size; // 分配块的总大小void* leftover_block = NULL; // 用于存储分割后剩余的内存块int leftover_size; // 剩余内存块的大小size = (size+7)&-8;    //将请求的大小调整为8字节对齐,并加上8字节用于内部管理标签size += 8;   // 确保请求的大小至少为24字节if (size < 24)size = 24;  //printf("malloc(0x%x)\n",size);// 清空或重置内存管理中的某个数据结构(blob)emptyblob();// 在空闲块树中查找适合分配的块block = freetree_locate(size);// 如果未找到合适的块if (block == NULL){// 扩展堆内存block = (char *)mem_heap_hi()+1; // 新块起始位置block_size = size; // 请求的大小block_size += HEAP_GROWSIZE; // 额外增加预设的堆增长量block_size &= -HEAP_GROWSIZE;  // 确保增长后的大小满足特定对齐要求// 尝试通过系统调用增加堆内存if (mem_sbrk(block_size) == (void *)-1){// 内存不足,无法满足请求return NULL;}*boundtag_hi = 0; //*((int*)(mem_heap_hi()-3)) = NULL;      // 更新堆的上界标记// 如果新块前面的块也是空闲的,则合并这两个块if (IsFree(PrevBlock(block))){block = PrevBlock(block);block_size += Size(block);delFromWherever(block); // 从管理结构中移除已合并的前一块}}else // 找到了合适的空闲块{block_size = Size(block); // 获取找到的块的大小delFromWherever(block); // 从空闲列表中移除这个块}// 如果分配的块比实际需要的大很多(超过24字节),考虑分割if ((block_size - size)>24)  {// 根据split_parity策略决定分割方向if (split_parity){leftover_size  = block_size - size;block_size     = size;   // 分割后当前块的大小leftover_block = (char *)block + block_size;  // 分割后剩余部分的地址queueNewFreeBlock(leftover_block,leftover_size); // 剩余部分重新加入空闲块管理}else{leftover_size = block_size - size;block_size    = size;leftover_block = (char *)block + leftover_size; queueNewFreeBlock(block,leftover_size); // 当前块成为剩余部分,重新加入管理block = leftover_block; // 更新block为实际分配的块地址}split_parity = !split_parity; // 切换下一次分割的方向}// 设置块的大小和分配状态标签setTags(block,block_size,0); //printBlock(block);// 调试代码:打印堆信息、所有块信息及红黑树验证
#if _DEBUG_if (printHeap());printAllBlocks();isRedBlack();
#endif //_DEBUG_// 返回分配的内存块地址return block;
}

相关文章:

计算机系统基础实训七-MallocLab实验

实验目的与要求 1、让学生理解动态内存分配的工作原理&#xff1b; 2、让学生应用指针、系统级编程的相关知识&#xff1b; 3、让学生应用各种动态内存分配器的实现方法&#xff1b; 实验原理与内容 &#xff08;1&#xff09;动态内存分配器基本原理 动态内存分配器维护…...

周末总结(2024/06/22)

工作 人际关系核心实践&#xff1a; 要学会随时回应别人的善意&#xff0c;执行时间控制在5分钟以内 坚持每天早会打招呼 遇到接不住的话题时拉低自己&#xff0c;抬高别人(无阴阳气息) 工作上的要点 现状&#xff08;接受破烂现状&#xff0c;改变状态&#xff09; - 这周没…...

2024.06.22【读书笔记】丨生物信息学与功能基因组学(第十七章 人类基因组 第二部分)【AI测试版】

第二部分:人类基因组的主要结论与网络资源 摘要: 第二部分深入总结了人类基因组计划的关键发现,并介绍了用于探索人类基因组的网络资源。这些结论不仅为我们理解人类生物学提供了新的视角,而且揭示了人类基因组的复杂性和动态性。 学习目标: 掌握人类基因组计划的主要科…...

SpringCloud-nacos基础

SpringCloud-nacos nacos在微服务种有两大作用&#xff1a; 配置中心服务注册中心 配置中心 维度管理 nacos配置中心可以在三个维度进行管理&#xff1a; spring.profiles.active dev/prod/test,通过这个属性可以配置不同环境下的配置文件。 配置的文件名应该为${spring…...

git的Cherry pick

Cherry pick Git Cherry Pick详解 https://blog.csdn.net/jam_yin/article/details/131594716 目标: 将开发分支A中提交的部分内容合并到B分支(可能是测试分支) 步骤: vscode安装 点击下图标进入graph...

LLC开关电源开发:第四节,LLC软件设计报告

LLC源代码链接 数控全桥LLC开发板软件设计报告  1. LLC硬件及软件框架2. LLC软件设计2.1 工程文件说明2.2 LLC中断设计2.2.1 20us中断2.2.2 5ms中断 2.3 LLC状态机设计2.3.1 初始化状态2.3.2 空闲状态2.3.3 软启动状态2.3.4 正常运行状态2.3.5 故障状态 2.4 环路设计2.4.1 环路…...

力扣85.最大矩形

力扣85.最大矩形 遍历所有行作为底边 做求矩形面积&#xff08;84. class Solution {public:int maximalRectangle(vector<vector<char>>& matrix) {if (matrix.empty()) return 0;int n matrix.size(),m matrix[0].size();int res0;vector<int> li…...

和琪宝的厦门之旅~

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 引言 承接去年国庆的遗憾&#xff0c;我们将这次的旅行城市定为厦门。 琪宝是下午四点左右到…...

4、MFC:菜单栏、工具栏与状态栏

菜单栏、工具栏与状态栏 1、菜单栏1.1 简介1.2 创建属性设置菜单消息成员函数 1.3 实例 2、工具栏2.1 简介工具栏属性2.2 创建消息CToolBar类的主要成员函数 2.3 实例 3、状态栏3.1 简介3.2 创建CStatusBar类状态栏创建 3.3 实例 1、菜单栏 1.1 简介 菜单在界面设计中是经常使…...

Java中的动态代理:原理与应用

Java中的动态代理&#xff1a;原理与应用 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java开发中&#xff0c;动态代理是一种强大且灵活的技术&#xff…...

DataWhale - 吃瓜教程学习笔记(二)

学习视频&#xff1a;第3章-一元线性回归_哔哩哔哩_bilibili 西瓜书对应章节&#xff1a; 3.1 - 3.2 一元线性回归 - 最小二乘法 - 极大似然估计 - 梯度 多元函数的一阶导数 - 海塞矩阵 多元函数的二阶导数 - 机器学习三要素...

[保姆级教程]uniapp自定义标签页切换组件

文章目录 导文样式改成动态列表切换点击效果加上点击自动滑动scroll-view加上切换组件效果 导文 unaipp自带的标签页和ui设计相差太大&#xff0c;直接修改组件比手写一个还麻烦&#xff0c;下面手写一个。 样式 先用scroll-view做一个滑动&#xff0c;不然多的话滑动不了。 &l…...

4种典型家庭教育方式,无论开始是哪一种,都会过渡到最后一种

家庭教育&#xff0c;是孩子教育的一个重要组成部分&#xff0c;事实上是对孩子影响最大的一种教育方式&#xff0c;绝大部分家庭教育都是由孩子的父母来完成的。 家庭教育的特点 家庭教育具有很明显的启蒙性、长期性、全面性。 1.启蒙性。我们的孩子对外部世界的认识和了解&am…...

[Django学习]查询过滤器(lookup types)

1.exact exact用于精确匹配字段的值。适用于需要精确查找某个字段值的场景。 Book.objects.filter(title__exactHarry Potter) 上面的查询会查找标题完全为“Harry Potter”的书籍。 2.iexact iexact忽略大小写地精确匹配字段的值。适用于需要忽略大小写进行精确匹配的场…...

异步开发的终极答案—协程

我们在之前的文章中讲过,在并发场景下,传统的基于多线程的命令式开发模型虽然比较简单,但并发数高了之后资源占用较高,大量线程会阻塞;而响应式编程模式我们可以通过异步化处理提升系统资源的利用效率,但异步开发有违人的直觉,门槛比较高。作为成年人,我们肯定希望全都…...

构建高效的大数据量延迟任务调度平台

目录 引言系统需求分析系统架构设计 总体架构任务调度模块任务存储模块任务执行模块 任务调度算法 时间轮算法优先级队列分布式锁 数据存储方案 关系型数据库NoSQL数据库混合存储方案 容错和高可用性 主从复制数据备份与恢复故障转移 性能优化 水平扩展缓存机制异步处理 监控与…...

Python武器库开发-武器库篇之ThinkPHP 2.x 任意代码执行漏洞(六十三)

Python武器库开发-武器库篇之ThinkPHP 2.x 任意代码执行漏洞&#xff08;六十三&#xff09; PHP代码审计简介 PHP代码审计是指对PHP程序进行安全审计&#xff0c;以发现潜在的安全漏洞和风险。PHP是一种流行的服务器端脚本语言&#xff0c;广泛用于开发网站和Web应用程序。由…...

SQLite数据库(数据库和链表双向转换)

文章目录 SQLite数据库一、SQLite简介1、SQLite和MySQL2、基于嵌入式的数据库 二、SQLite数据库安装三、SQLite的常用命令四、SQLite的编程操作1、SQLite数据库相关API&#xff08;1&#xff09;头文件&#xff08;2&#xff09;sqlite3_open()&#xff08;3&#xff09;sqlite…...

React框架的来龙去脉,react的技术原理及技术难点和要点,小白的进阶之路

React 框架的来龙去脉&#xff1a;技术原理及技术难点和要点 1. React 的起源与发展 React 是由 Facebook 开发的一个用于构建用户界面的 JavaScript 库。它最初由 Jordan Walke 创建&#xff0c;并在 2013 年开源。React 的出现是为了解决在大型应用中管理复杂用户界面的问题…...

CPU飙升100%怎么办?字节跳动面试官告诉你答案!

小北说在前面 CPU占用率突然飙升是技术人员常遇到的一个棘手问题&#xff0c;它是一个与具体技术无关的普遍挑战。 这个问题可以很简单&#xff0c;也可以相当复杂。 有时候&#xff0c;只是一个死循环在作祟。 有时候&#xff0c;是死锁导致的。 有时候&#xff0c;代码中有…...

物理层(二)

2.2 传输介质 2.2.1 双绞线、同轴电缆、光纤和无线传输介质 传输介质也称传输媒体&#xff0c;是数据传输系统中发送器和接收器之间的物理通路。传输介质可分为:①导向传输介质&#xff0c;指铜线或光纤等&#xff0c;电磁波被导向为沿着固体介质传播:②)非导向传输介质&…...

C#——文件读取IO操作File类详情

文件读取操作 IO类 就是对应文件的操作的类I/O类 包含各种不同的类 用于执行各种文件操作&#xff0c;创建文件删除文件读写文件 常用的类: File处理文件操作的类 FilleStream用于文件当中任何位置的读写 File类 1.文件创建 File.Create() 在指定路径下创建…...

昨天gitee网站访问不了,开始以为电脑哪里有问题了

昨天gitee网站下午访问不了&#xff0c;开始以为是什么毛病。 结果同样的网络&#xff0c;手机是可以访问的。 当然就ping www.gitee.com 结果也下面那样是正常的 以为是好的&#xff0c;但就是访问www.gitee.com也是不行&#xff0c;后来用阿里云的服务器curl访问是下面情况&…...

深入理解适配器模式:Java实现与框架应用

适配器模式是一种结构型设计模式&#xff0c;它允许将一个类的接口转换成客户端希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类可以协同工作。在本篇博客中&#xff0c;我们将详细介绍适配器模式&#xff0c;并演示如何在Java中实现它。最后&#xff0…...

跌倒识别:守护公共安全的AI技术应用场景-免费API调用

随着科技的不断进步&#xff0c;人工智能在各个领域的应用日益广泛&#xff0c;其中在公共安全领域&#xff0c;智能跌倒识别系统正逐渐成为守护人们安全的重要工具。本文将分享智能跌倒识别系统在不同场景下的应用及其重要性。 产品在线体验地址-API调用或本地化部署 AI算法模…...

算法:渐进记号的含义及时间复杂度计算

渐进记号及时间复杂度计算 渐近符号渐近记号 Ω \Omega Ω渐进记号 Θ \Theta Θ渐进记号小 ο \omicron ο渐进记号小 ω \omega ω渐进记号大 O \Omicron O常见的时间复杂度关系 时间复杂度计算&#xff1a;递归方程代入法迭代法套用公式法 渐近符号 渐近记号 Ω \Omega Ω …...

idea导入文件里面的子模块maven未识别处理解决办法

1、File → Project Structure → 点击“Modules” → 点击“” → “Import Model” 2、可以看到很多子模块&#xff0c;选择子模块下的 pom.xml 文件导入一个一个点累死了&#xff0c;父目录下也没有pom文件 解决办法&#xff1a;找到子模块中有一个pom.xml文件&#xff0c;…...

IOS Swift 从入门到精通:协议和扩展

文章目录 协议协议继承扩展协议扩展面向协议的编程总结&#xff1a; 今天你将学习一些真正的 Swifty 功能&#xff1a;协议和面向协议的编程&#xff08;POP&#xff09;。 POP 摒弃了庞大而复杂的继承层次结构&#xff0c;代之以更小、更简单、可以组合在一起的协议。这确实应…...

Vue插件开发:Vue.js的插件架构允许开发者扩展Vue的核心功能,我们可以探讨如何开发一个Vue插件并与社区分享

了解Vue插件 Vue插件的概念: Vue插件用于为Vue.js添加全局级别的功能。它提供了一种开箱即用的机制来应用全局性的功能扩展。这些插件通常用来将全局方法或属性,组件选项,Vue实例的方法,或者注入一些组件选项比如mixins和自定义方法添加至Vue.js。 Vue插件的使用场景:…...

学习面向对象前--Java基础练习题

前言 写给所有一起努力学习Java的朋友们&#xff0c;敲代码本身其实是我们梳理逻辑的一个过程。我们在学习Java代码的过程中&#xff0c;除了需要学习Java的一些基本操作及使用&#xff0c;更重要的是我们需要培养好的逻辑思维。逻辑梳理好之后&#xff0c;我们编写代码实现需要…...

用Python实现抖音新作品监控助手,实时获取博主动态

声明&#xff1a; 本文以教学为基准、本文提供的可操作性不得用于任何商业用途和违法违规场景。本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。包含关注&#xff0c;点赞等 该项目的主要功能是通过Python代码&…...

图像分隔和深度成像技术为什么受市场欢迎-数字孪生技术和物联网智能汽车技术的大爆发?分析一下图像技术的前生后世

图像分隔和深度成像是计算机视觉和图像处理领域的两项重要技术&#xff0c;它们各自有不同的技术基础和要点。 图像分隔技术基础&#xff1a; 机器学习和模式识别&#xff1a; 图像分隔通常依赖于机器学习算法&#xff0c;如支持向量机&#xff08;SVM&#xff09;、随机森林…...

Redis 内存策略

一、Redis 内存回收 Redis 之所以性能强&#xff0c;最主要的原因就是基于内存存储。然而单节点的 Redis 其内存大小不宜过大&#xff0c;会影响持久化或主从同步性能。 我们可以通过修改配置文件来设置 Redis 的最大内存&#xff1a; # 格式&#xff1a; # maxmemory <byt…...

Java小实验————斗地主

早期使用的JavaSE用到的技术栈有&#xff1a;Map集合,数组&#xff0c;set集合&#xff0c;只是简单实现了斗地主的模拟阶段&#xff0c;感兴趣的小伙伴可以调试增加功能 代码如下&#xff1a; import java.util.*;public class Poker {public static void main(String[] arg…...

【Oracle】Linux 卸载重装 oracle 教程(如何清理干净残留)系统 CentOS7.6

总览 1.停止监听 2.删除 Oracle 数据库实例 3.删除 Oracle 相关服务 4.删除 Oracle 服务脚本 5.清理 Oracle 软件和配置文件 6.强制卸载 Oracle 软件包 一、开始干活&#xff08;所有操作使用 root 权限&#xff0c;在 root 用户下执行&#xff09; 1.停止监听 lsnrctl sto…...

web中间件漏洞-Jenkins漏洞-弱口令、反弹shell

web中间件漏洞-Jenkins漏洞-弱口令、反弹shell Jenkins弱口令 默认用户一般为jenkins/jenkins 使用admin/admin123登陆成功 Jenkins反弹shell 格式为 println"命令".execute().text 在/tmp目录中生成shell.sh文件&#xff0c;并向其中写入反弹shell的语句 new…...

Linux开发讲课9--- Linux的IPC机制-内存映射(Memory Mapping)

Linux的IPC&#xff08;Inter-Process Communication&#xff0c;进程间通信&#xff09;机制是多个进程之间相互沟通的方法&#xff0c;它允许不同进程之间传播或交换信息。Linux支持多种IPC方式&#xff0c;包括但不限于&#xff1a; 管道&#xff08;Pipe&#xff09;&#…...

Java赋值运算符

Java赋值运算符分为以下&#xff1a; 符号 作用 说明 赋值 int a 10,把10赋值给变量a 加后赋值 ab,将ab的值赋值给变量a - 减后赋值 a-b,将a-b的值赋值给变量a* 乘后赋值 a*b,将a*b的值赋值给变量a / 除后赋值 a/b,将a/b的值赋值给变量a % 取余赋值 a%b,将a%b的值赋值给变量…...

Qt做群控系统

群控系统顾名思义&#xff0c;一台设备控制多台机器。首先我们来创造下界面。我们通过QT UI设计界面。设计界面如下&#xff1a; 登录界面&#xff1a; 登录界面分为两种角色&#xff0c;一种是管理员&#xff0c;另一种是超级管理员。两种用户的主界面是不同的。通过选中记住…...

【专业英语 复习】第10章 Information System

1. 单选题 (1分) An example of this type of report would be a sales report that shows that certain items are selling significantly above or below forecasts. () A. Inventory B. Demand C. Periodic D. Exception 正确答案&#xff1a; D 这种类型的报…...

09-axios在Vue中的导入与配置

09-axios 前言首先简单了解什么是Axios&#xff1f;以上完成后就可以使用了 前言 我们接着上一篇文章 08-路由地址的数据获取 来讲。 下一篇文章 10-vuex在Vue中的导入与配置 首先简单了解什么是Axios&#xff1f; Axios是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端…...

odoo17 小变更4

odoo17 小变更4 1、代码中去除了访问私人地址权限,但翻译中均还有,怪不 model:res.groups,name:base.group_private_addresses msgid "Access to Private Addresses" msgstr "" 代码也查看了,的确没有了此权限组 --><record model="res.g…...

Flink assignTimestampsAndWatermarks 深度解析:时间语义与水印生成

目录 概述 时间语义 时间戳分配 水印的作用 最佳实践 案例分析 注意事项 应用场景 概述 在Apache Flink中,assignTimestampsAndWatermarks是一个重要的方法,它允许数据流处理程序根据事件时间(event time)分配时间戳和生成水印(watermarks)。这个方法通常用于处理…...

C++排序算法——合并有序数组

合并有序数组 思路 我们可以设想一个排序的函数 这个函数里 我们有三个while while(第一次的执行条件) {先进行第一次的合并 } while(第二次的合并条件) { 把a数组在第一次没有排序上的给加进去 }while(第三次的合并条件) { 把b数组在第一次没有排序上的给加进去 }看完了这个…...

安装pytorch环境

安装&#xff1a;Anaconda3 通过命令行查显卡nvidia-smi 打开Anacanda prompt 新建 conda create -n pytorch python3.6 在Previous PyTorch Versions | PyTorch选择1.70&#xff0c;安装成功&#xff0c;但torch.cuda.is_available 返回false conda install pytorch1.7.0…...

内卷从古到今就一直存在,并不是近年的“新物”,破局在于你是否有意识地学习。

一.背景&#xff1a; 反思自己过去从学生时代到职场时代。“内卷”其实已经一直存在&#xff0c;从古到今都一直存在&#xff0c;也并不是近几年产出的“新物”。已经连续5年高考人数在1000万以上&#xff0c;而今年1300多万达到新高&#xff0c;对于竞争压力如此之大&#xf…...

跟《经济学人》学英文:2024年6月15日这期 The war for AI talent is heating up

The war for AI talent is heating up Big tech firms scramble to fill gaps as brain drain sets in 争夺人工智能人才的战争正在升温 随着人才流失的到来&#xff0c;大型科技公司争相填补空缺 brain drain&#xff1a;人才流失 scramble&#xff1a;争夺&#xff1b;争…...

港湾周评|高盛眼中的618增长

《港湾商业观察》李镭 年中最重要的购物节618终于尘埃落定了。2024年的618各大电商平台竞技情况如何&#xff1f;又有哪些新的亮点&#xff1f;都成为外界观察消费行为的参考指标。 根据京东618数据显示&#xff1a;累计成交额过10亿的品牌83个&#xff0c;超15万个中小商家销…...

SPSS知识

特点 SPSS的一些特点&#xff1a; 分析结果清晰、直观&#xff1a;SPSS提供了丰富的图表和表格&#xff0c;可以帮助用户直观地理解数据分析的结果。分析结果通常包含详细的统计量、图形和文本描述&#xff0c;使得分析结果易于解释。 易学易用&#xff1a;SPSS的用户界面设计…...

【网络安全的神秘世界】关于Linux中一些好玩的字符游戏

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 佛祖保佑 把 motd 通过xtp拖到Linux中 liyangUbuntu2204:~$ cp motd /etc/motd #一定要放在etc下 liyangUbuntu2204:~$ exi…...