计算机系统基础实训七-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 x
    y->Parent = x->Parent;
    if ( x->Parent == NULL )
	*treeroot = y;
    else
	if ( x == x->Parent->Left )
	    //x was on the left of its parent
	    x->Parent->Left = y;
	else
	    //x must have been on the right
	    x->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 x
    if (x->Parent == NULL) 
	*treeroot = y;
    else
	if (x == x->Parent->Left)
	    // x was on the left of its parent
	    x->Parent->Left = y;
	else
	    // x must have been on the right
	    x->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 pointer
  if (current == NULL) {
      *treeroot = x;
      x->Parent = NULL;
      x->Right = x->Left = NULL;
      x->Color = BLACK;
      return 0;
  }

  //Non-empty tree --> insert as leaf
  while(1) {
      if (isLess(x,current)) {
	  //x belongs in the left child of current node
	  if (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 node
	  if (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 block
    setTags(ptr, size, IN_TREE);
    x = (Node*)ptr;

    //Do a tree insertion
    if (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;
	}
	else
	    current = 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);
		else
		    right_child_is2x(x->Parent);
	    }
	    return;
	}
    }

    if (isBlack(sis->Right))  //farther child is black
    {
	//make it so that the farther child is red
	right_rotate(sis);
	sis->Color = RED;   //used to be black, old sis
	sis = 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);
		else
		    right_child_is2x(x->Parent);
	    }
	    return;
	}
    }

    if (isBlack(sis->Left))  //farther child is black
    {
	//make it so that the farther child is red
	left_rotate(sis);
	sis->Color = RED;   //used to be black, old sis
	sis = 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 black

	if (child)
	    child->Parent = z->Parent;
	
	if (z->Parent == NULL)
	    *treeroot = child;
	else if (z->Parent->Left == z)
	    z->Parent->Left = child;
	else
	    z->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-null
	z->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;
	else
	    z->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;
	else
	    z->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;
	else
	    z->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,释放原内存并返回NULL
    if (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) {
            // 扩展失败,返回NULL
            return 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/734101.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

外包IT运维解决方案

随着企业信息化进程的不断深入&#xff0c;IT系统的复杂性和重要性日益增加。高效的IT运维服务对于保证业务连续性、提升企业竞争力至关重要。外包IT运维解决方案通过专业的服务和技术支持&#xff0c;帮助企业降低运维成本、提高运维效率和服务质量。 本文结合《外包IT运维解…

咖啡事故,上海Manner咖啡店,1天两起店员和顾客发生冲突

上海咖啡店Manner&#xff0c;一天的时间竟然发生两起店员和员工发生肢体冲突&#xff1a; 事情详情&#xff1a; Manner威海路716店事件: 店员泼顾客咖啡粉&#xff0c;随后被辞退品牌方回应媒体&#xff0c;表示将严肃处理Manner梅花路门店事件:顾客因等待时间长抱怨&…

Aquila-Med LLM:开创性的全流程开源医疗语言模型

​论文链接&#xff1a;https://arxiv.org/pdf/2406.12182 开源链接&#xff1a;https://huggingface.co/BAAI/AquilaMed-RL http://open.flopsera.com/flopsera-open/details/AquilaMed_SFT http://open.flopsera.com/flopsera-open/details/AquilaMed_DPO 近年来&#xf…

Magento1与Magento2的区别

本人接触magento有些年头了。。。 2012年开始用magento 1.7。2016年开始用magento2.0。 截止到目前。M1最新版本是1.9.3.3。 M2最新版本是2.2.2。 想当年第一次接触magento的时候&#xff0c;是跟同事一起&#xff0c;网上下载的Alan Storm的深入理解magento系统&#xff0c;…

链表中环的入口节点

链表中环的入口节点 描述 链表中环的入口节点 给一个长度为n链表&#xff0c;若其中包含环&#xff0c;请找出该链表的环的入口结点&#xff0c;否则&#xff0c;返回null。 数据范围&#xff1a; n≤10000&#xff0c; 1<结点值<10000 要求&#xff1a;空间复杂度 O(1)…

windows下mysql修改 my.ini的datadir后 `Access denied`

1. 背景 window安装mysql数据库时,不能指定数据文件存放位置(默认安装路径 "C:/ProgramData")。 只能通过修改mysql.ini来更改数据文件存放目录。 2. 问题: 修改mysql.ini后,mysql 出现 "Access denied for user ‘root‘@‘localhost‘ (using passwor…

webpack安装sass

package.json文件 {"devDependencies": {"sass-loader": "^7.2.0","sass": "^1.22.10","fibers": "^4.0.1"} }这个不用webpack.config.js module.exports {module: {rules: [{test: /\.s[ac]ss$/i,u…

算法设计与分析:分治法求最近点对问题

目录 一、实验目的 二、实验内容 三、算法思想 四、实验步骤 1、蛮力法 2、分治法 2.1 先用快速排序SortX(A,1,n)将所有点按x坐标升序排序 2.2 点数n<3时直接计算&#xff0c;时间复杂度为O(1) 2.3 点数n>3时 五、实验结果和分析 一、实验目的 1. 掌握分治法思…

Ilya出走记:SSI的超级安全革命

图片&#xff5c;OpenAI官网 ©自象限原创 作者丨罗辑、程心 和OpenAI分道扬镳以后&#xff0c;Ilya“神秘而伟大”的事业终于揭开了面纱。 6月20日&#xff0c;前OpenAI核心创始人 Ilya Stuskever&#xff0c;在官宣离职一个月后&#xff0c;Ilya在社交媒体平台公开了…

SambaLingo——教会大模型新语言

在当今数字化时代&#xff0c;语言不仅是沟通的桥梁&#xff0c;也是信息和知识传递的核心。尽管大模型&#xff08;LLMs&#xff09;在处理英语等主流语言方面取得了显著进展&#xff0c;但它们在理解和生成其他语言内容方面的能力却参差不齐。这种不平衡限制了技术在全球范围…

Charles抓取安卓应用https包演示

一、准备软件 夜神安卓模拟器 (yeshen.com) Charles (charlesproxy.com) 二、配置抓包 2.1 Charles安装PC根证书 记住这里的ip端口 三、安卓模拟器配置 3.1 配置安卓客户端网络代理 填写上文的ip端口&#xff0c;保存 3.2 安装根证书 3.2.1 导出根证书 linux主机执行 op…

Springboot项目ES报异常query_shard_exception

详细异常信息如下&#xff1a; {"error": {"root_cause": [{"type": "query_shard_exception","reason": "failed to create query: {\n \"bool\" : {\n \"filter\" : [\n {\n \…

AST小工具|编写一个通用的js混淆代码美化工具

关注它&#xff0c;不迷路。 本文章中所有内容仅供学习交流&#xff0c;不可用于任何商业用途和非法用途&#xff0c;否则后果自负&#xff0c;如有侵权&#xff0c;请联系作者立即删除&#xff01; 一.问题 如题&#xff0c;如何编写一个通用的js混淆代码美化工具&…

R语言——R语言基础

1、用repeat、for、while计算从1-10的所有整数的平方和 2、编写一个函数&#xff0c;给出两个正整数&#xff0c;计算他们的最小公倍数 3、编写一个函数&#xff0c;让用户输入姓名、年龄&#xff0c;得出他明年的年龄。用paste打印出来。例如&#xff1a;"Hi xiaoming …

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

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

图扑助力铝型材挤压:数字孪生引领智慧管理

通过图扑数字孪生技术&#xff0c;为铝型材挤压车间提供实时监控和优化管理方案。高精度三维建模和数据可视化提升了生产效率和管理透明度&#xff0c;推动智能制造和资源优化配置。

关于运用人工智能帮助自己实现英语能力的有效提升?

# 实验报告 ## 实验目的 - 描述实验的目标&#xff1a;自己可以知道&#xff0c;自己的ai学习方法是否可以有效帮助自己实现自己的学习提升。 预期结果&#xff1a;在自己利用科技对于自己进行学习的过程中&#xff0c;自己的成长速度应该是一个幂指数的增长 ## 文献回顾 根据…

FilterSolutions滤波器设计应用

首先介绍4种滤波器&#xff1a; 1、贝赛尔(Bessel)滤波器是具有最大平坦的群延迟&#xff08;线性相位响应&#xff09;的线性过滤器。 2、巴特沃斯滤波器是电子滤波器的一种&#xff0c;巴特沃斯滤波器的特点是通频带的频率响应曲线最平滑。 3、切比雪夫滤波器&#xff0c;…

ffmpeg音视频开发从入门到精通——ffmpeg日志及目录操作

文章目录 FFMPEG1. 操作日志2. 文件移动和删除3. 操作目录重要函数 FFMPEG 1. 操作日志 日志级别 AV LOG ERROR AV LOG WARNING AV LOG INFO AV LOG DEBUG cmake_minimum_required(VERSION 3.27) project(FFmpeg_exercise) set(CMAKE_CXX_STANDARD 14)# 定义FFmpeg的安装路…

冲击2024年CSDN博客之星TOP1:CSDN文章质量分查询在哪里?

文章目录 一&#xff0c;2023年博客之星规则1&#xff0c;不高的入围门槛2&#xff0c;[CSDN博文质量分测评地址](https://www.csdn.net/qc) 二&#xff0c;高分秘籍1&#xff0c;要有目录2&#xff0c;文章长度要足够&#xff0c;我的经验是汉字加代码至少1000字。3&#xff0…